603face28d48cb1d464176bf65385bb805546005
[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         
12118         Roo.log("Toolbar Item - render");
12119         this.td = td;
12120         td.appendChild(this.el);
12121         
12122         this.fireEvent('render', this);
12123     },
12124     
12125     /**
12126      * Removes and destroys this item.
12127      */
12128     destroy : function(){
12129         this.td.parentNode.removeChild(this.td);
12130     },
12131     
12132     /**
12133      * Shows this item.
12134      */
12135     show: function(){
12136         this.hidden = false;
12137         this.td.style.display = "";
12138     },
12139     
12140     /**
12141      * Hides this item.
12142      */
12143     hide: function(){
12144         this.hidden = true;
12145         this.td.style.display = "none";
12146     },
12147     
12148     /**
12149      * Convenience function for boolean show/hide.
12150      * @param {Boolean} visible true to show/false to hide
12151      */
12152     setVisible: function(visible){
12153         if(visible) {
12154             this.show();
12155         }else{
12156             this.hide();
12157         }
12158     },
12159     
12160     /**
12161      * Try to focus this item.
12162      */
12163     focus : function(){
12164         Roo.fly(this.el).focus();
12165     },
12166     
12167     /**
12168      * Disables this item.
12169      */
12170     disable : function(){
12171         Roo.fly(this.td).addClass("x-item-disabled");
12172         this.disabled = true;
12173         this.el.disabled = true;
12174     },
12175     
12176     /**
12177      * Enables this item.
12178      */
12179     enable : function(){
12180         Roo.fly(this.td).removeClass("x-item-disabled");
12181         this.disabled = false;
12182         this.el.disabled = false;
12183     }
12184 });
12185
12186
12187 /**
12188  * @class Roo.Toolbar.Separator
12189  * @extends Roo.Toolbar.Item
12190  * A simple toolbar separator class
12191  * @constructor
12192  * Creates a new Separator
12193  */
12194 Roo.Toolbar.Separator = function(){
12195     var s = document.createElement("span");
12196     s.className = "ytb-sep";
12197     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12198 };
12199 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12200     enable:Roo.emptyFn,
12201     disable:Roo.emptyFn,
12202     focus:Roo.emptyFn
12203 });
12204
12205 /**
12206  * @class Roo.Toolbar.Spacer
12207  * @extends Roo.Toolbar.Item
12208  * A simple element that adds extra horizontal space to a toolbar.
12209  * @constructor
12210  * Creates a new Spacer
12211  */
12212 Roo.Toolbar.Spacer = function(){
12213     var s = document.createElement("div");
12214     s.className = "ytb-spacer";
12215     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12216 };
12217 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12218     enable:Roo.emptyFn,
12219     disable:Roo.emptyFn,
12220     focus:Roo.emptyFn
12221 });
12222
12223 /**
12224  * @class Roo.Toolbar.Fill
12225  * @extends Roo.Toolbar.Spacer
12226  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12227  * @constructor
12228  * Creates a new Spacer
12229  */
12230 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12231     // private
12232     render : function(td){
12233         td.style.width = '100%';
12234         Roo.Toolbar.Fill.superclass.render.call(this, td);
12235     }
12236 });
12237
12238 /**
12239  * @class Roo.Toolbar.TextItem
12240  * @extends Roo.Toolbar.Item
12241  * A simple class that renders text directly into a toolbar.
12242  * @constructor
12243  * Creates a new TextItem
12244  * @param {String} text
12245  */
12246 Roo.Toolbar.TextItem = function(text){
12247     if (typeof(text) == 'object') {
12248         text = text.text;
12249     }
12250     var s = document.createElement("span");
12251     s.className = "ytb-text";
12252     s.innerHTML = text;
12253     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12254 };
12255 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12256     
12257     render : function(td){
12258         this.td = td;
12259         Roo.Toolbar.TextItem.superclass.render.call(this, td);
12260     },
12261     
12262     enable:Roo.emptyFn,
12263     disable:Roo.emptyFn,
12264     focus:Roo.emptyFn
12265 });
12266
12267 /**
12268  * @class Roo.Toolbar.Button
12269  * @extends Roo.Button
12270  * A button that renders into a toolbar.
12271  * @constructor
12272  * Creates a new Button
12273  * @param {Object} config A standard {@link Roo.Button} config object
12274  */
12275 Roo.Toolbar.Button = function(config){
12276     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12277 };
12278 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12279     render : function(td){
12280         this.td = td;
12281         Roo.Toolbar.Button.superclass.render.call(this, td);
12282     },
12283     
12284     /**
12285      * Removes and destroys this button
12286      */
12287     destroy : function(){
12288         Roo.Toolbar.Button.superclass.destroy.call(this);
12289         this.td.parentNode.removeChild(this.td);
12290     },
12291     
12292     /**
12293      * Shows this button
12294      */
12295     show: function(){
12296         this.hidden = false;
12297         this.td.style.display = "";
12298     },
12299     
12300     /**
12301      * Hides this button
12302      */
12303     hide: function(){
12304         this.hidden = true;
12305         this.td.style.display = "none";
12306     },
12307
12308     /**
12309      * Disables this item
12310      */
12311     disable : function(){
12312         Roo.fly(this.td).addClass("x-item-disabled");
12313         this.disabled = true;
12314     },
12315
12316     /**
12317      * Enables this item
12318      */
12319     enable : function(){
12320         Roo.fly(this.td).removeClass("x-item-disabled");
12321         this.disabled = false;
12322     }
12323 });
12324 // backwards compat
12325 Roo.ToolbarButton = Roo.Toolbar.Button;
12326
12327 /**
12328  * @class Roo.Toolbar.SplitButton
12329  * @extends Roo.SplitButton
12330  * A menu button that renders into a toolbar.
12331  * @constructor
12332  * Creates a new SplitButton
12333  * @param {Object} config A standard {@link Roo.SplitButton} config object
12334  */
12335 Roo.Toolbar.SplitButton = function(config){
12336     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12337 };
12338 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12339     render : function(td){
12340         this.td = td;
12341         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12342     },
12343     
12344     /**
12345      * Removes and destroys this button
12346      */
12347     destroy : function(){
12348         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12349         this.td.parentNode.removeChild(this.td);
12350     },
12351     
12352     /**
12353      * Shows this button
12354      */
12355     show: function(){
12356         this.hidden = false;
12357         this.td.style.display = "";
12358     },
12359     
12360     /**
12361      * Hides this button
12362      */
12363     hide: function(){
12364         this.hidden = true;
12365         this.td.style.display = "none";
12366     }
12367 });
12368
12369 // backwards compat
12370 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12371  * Based on:
12372  * Ext JS Library 1.1.1
12373  * Copyright(c) 2006-2007, Ext JS, LLC.
12374  *
12375  * Originally Released Under LGPL - original licence link has changed is not relivant.
12376  *
12377  * Fork - LGPL
12378  * <script type="text/javascript">
12379  */
12380  
12381 /**
12382  * @class Roo.PagingToolbar
12383  * @extends Roo.Toolbar
12384  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12385  * @constructor
12386  * Create a new PagingToolbar
12387  * @param {Object} config The config object
12388  */
12389 Roo.PagingToolbar = function(el, ds, config)
12390 {
12391     // old args format still supported... - xtype is prefered..
12392     if (typeof(el) == 'object' && el.xtype) {
12393         // created from xtype...
12394         config = el;
12395         ds = el.dataSource;
12396         el = config.container;
12397     }
12398     var items = [];
12399     if (config.items) {
12400         items = config.items;
12401         config.items = [];
12402     }
12403     
12404     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12405     this.ds = ds;
12406     this.cursor = 0;
12407     this.renderButtons(this.el);
12408     this.bind(ds);
12409     
12410     // supprot items array.
12411    
12412     Roo.each(items, function(e) {
12413         this.add(Roo.factory(e));
12414     },this);
12415     
12416 };
12417
12418 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12419     /**
12420      * @cfg {Roo.data.Store} dataSource
12421      * The underlying data store providing the paged data
12422      */
12423     /**
12424      * @cfg {String/HTMLElement/Element} container
12425      * container The id or element that will contain the toolbar
12426      */
12427     /**
12428      * @cfg {Boolean} displayInfo
12429      * True to display the displayMsg (defaults to false)
12430      */
12431     /**
12432      * @cfg {Number} pageSize
12433      * The number of records to display per page (defaults to 20)
12434      */
12435     pageSize: 20,
12436     /**
12437      * @cfg {String} displayMsg
12438      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12439      */
12440     displayMsg : 'Displaying {0} - {1} of {2}',
12441     /**
12442      * @cfg {String} emptyMsg
12443      * The message to display when no records are found (defaults to "No data to display")
12444      */
12445     emptyMsg : 'No data to display',
12446     /**
12447      * Customizable piece of the default paging text (defaults to "Page")
12448      * @type String
12449      */
12450     beforePageText : "Page",
12451     /**
12452      * Customizable piece of the default paging text (defaults to "of %0")
12453      * @type String
12454      */
12455     afterPageText : "of {0}",
12456     /**
12457      * Customizable piece of the default paging text (defaults to "First Page")
12458      * @type String
12459      */
12460     firstText : "First Page",
12461     /**
12462      * Customizable piece of the default paging text (defaults to "Previous Page")
12463      * @type String
12464      */
12465     prevText : "Previous Page",
12466     /**
12467      * Customizable piece of the default paging text (defaults to "Next Page")
12468      * @type String
12469      */
12470     nextText : "Next Page",
12471     /**
12472      * Customizable piece of the default paging text (defaults to "Last Page")
12473      * @type String
12474      */
12475     lastText : "Last Page",
12476     /**
12477      * Customizable piece of the default paging text (defaults to "Refresh")
12478      * @type String
12479      */
12480     refreshText : "Refresh",
12481
12482     // private
12483     renderButtons : function(el){
12484         Roo.PagingToolbar.superclass.render.call(this, el);
12485         this.first = this.addButton({
12486             tooltip: this.firstText,
12487             cls: "x-btn-icon x-grid-page-first",
12488             disabled: true,
12489             handler: this.onClick.createDelegate(this, ["first"])
12490         });
12491         this.prev = this.addButton({
12492             tooltip: this.prevText,
12493             cls: "x-btn-icon x-grid-page-prev",
12494             disabled: true,
12495             handler: this.onClick.createDelegate(this, ["prev"])
12496         });
12497         //this.addSeparator();
12498         this.add(this.beforePageText);
12499         this.field = Roo.get(this.addDom({
12500            tag: "input",
12501            type: "text",
12502            size: "3",
12503            value: "1",
12504            cls: "x-grid-page-number"
12505         }).el);
12506         this.field.on("keydown", this.onPagingKeydown, this);
12507         this.field.on("focus", function(){this.dom.select();});
12508         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12509         this.field.setHeight(18);
12510         //this.addSeparator();
12511         this.next = this.addButton({
12512             tooltip: this.nextText,
12513             cls: "x-btn-icon x-grid-page-next",
12514             disabled: true,
12515             handler: this.onClick.createDelegate(this, ["next"])
12516         });
12517         this.last = this.addButton({
12518             tooltip: this.lastText,
12519             cls: "x-btn-icon x-grid-page-last",
12520             disabled: true,
12521             handler: this.onClick.createDelegate(this, ["last"])
12522         });
12523         //this.addSeparator();
12524         this.loading = this.addButton({
12525             tooltip: this.refreshText,
12526             cls: "x-btn-icon x-grid-loading",
12527             handler: this.onClick.createDelegate(this, ["refresh"])
12528         });
12529
12530         if(this.displayInfo){
12531             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12532         }
12533     },
12534
12535     // private
12536     updateInfo : function(){
12537         if(this.displayEl){
12538             var count = this.ds.getCount();
12539             var msg = count == 0 ?
12540                 this.emptyMsg :
12541                 String.format(
12542                     this.displayMsg,
12543                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12544                 );
12545             this.displayEl.update(msg);
12546         }
12547     },
12548
12549     // private
12550     onLoad : function(ds, r, o){
12551        this.cursor = o.params ? o.params.start : 0;
12552        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12553
12554        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12555        this.field.dom.value = ap;
12556        this.first.setDisabled(ap == 1);
12557        this.prev.setDisabled(ap == 1);
12558        this.next.setDisabled(ap == ps);
12559        this.last.setDisabled(ap == ps);
12560        this.loading.enable();
12561        this.updateInfo();
12562     },
12563
12564     // private
12565     getPageData : function(){
12566         var total = this.ds.getTotalCount();
12567         return {
12568             total : total,
12569             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12570             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12571         };
12572     },
12573
12574     // private
12575     onLoadError : function(){
12576         this.loading.enable();
12577     },
12578
12579     // private
12580     onPagingKeydown : function(e){
12581         var k = e.getKey();
12582         var d = this.getPageData();
12583         if(k == e.RETURN){
12584             var v = this.field.dom.value, pageNum;
12585             if(!v || isNaN(pageNum = parseInt(v, 10))){
12586                 this.field.dom.value = d.activePage;
12587                 return;
12588             }
12589             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12590             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12591             e.stopEvent();
12592         }
12593         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))
12594         {
12595           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12596           this.field.dom.value = pageNum;
12597           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12598           e.stopEvent();
12599         }
12600         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12601         {
12602           var v = this.field.dom.value, pageNum; 
12603           var increment = (e.shiftKey) ? 10 : 1;
12604           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12605             increment *= -1;
12606           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12607             this.field.dom.value = d.activePage;
12608             return;
12609           }
12610           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12611           {
12612             this.field.dom.value = parseInt(v, 10) + increment;
12613             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12614             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12615           }
12616           e.stopEvent();
12617         }
12618     },
12619
12620     // private
12621     beforeLoad : function(){
12622         if(this.loading){
12623             this.loading.disable();
12624         }
12625     },
12626
12627     // private
12628     onClick : function(which){
12629         var ds = this.ds;
12630         switch(which){
12631             case "first":
12632                 ds.load({params:{start: 0, limit: this.pageSize}});
12633             break;
12634             case "prev":
12635                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12636             break;
12637             case "next":
12638                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12639             break;
12640             case "last":
12641                 var total = ds.getTotalCount();
12642                 var extra = total % this.pageSize;
12643                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12644                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12645             break;
12646             case "refresh":
12647                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12648             break;
12649         }
12650     },
12651
12652     /**
12653      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12654      * @param {Roo.data.Store} store The data store to unbind
12655      */
12656     unbind : function(ds){
12657         ds.un("beforeload", this.beforeLoad, this);
12658         ds.un("load", this.onLoad, this);
12659         ds.un("loadexception", this.onLoadError, this);
12660         ds.un("remove", this.updateInfo, this);
12661         ds.un("add", this.updateInfo, this);
12662         this.ds = undefined;
12663     },
12664
12665     /**
12666      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12667      * @param {Roo.data.Store} store The data store to bind
12668      */
12669     bind : function(ds){
12670         ds.on("beforeload", this.beforeLoad, this);
12671         ds.on("load", this.onLoad, this);
12672         ds.on("loadexception", this.onLoadError, this);
12673         ds.on("remove", this.updateInfo, this);
12674         ds.on("add", this.updateInfo, this);
12675         this.ds = ds;
12676     }
12677 });/*
12678  * Based on:
12679  * Ext JS Library 1.1.1
12680  * Copyright(c) 2006-2007, Ext JS, LLC.
12681  *
12682  * Originally Released Under LGPL - original licence link has changed is not relivant.
12683  *
12684  * Fork - LGPL
12685  * <script type="text/javascript">
12686  */
12687
12688 /**
12689  * @class Roo.Resizable
12690  * @extends Roo.util.Observable
12691  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12692  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12693  * 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
12694  * the element will be wrapped for you automatically.</p>
12695  * <p>Here is the list of valid resize handles:</p>
12696  * <pre>
12697 Value   Description
12698 ------  -------------------
12699  'n'     north
12700  's'     south
12701  'e'     east
12702  'w'     west
12703  'nw'    northwest
12704  'sw'    southwest
12705  'se'    southeast
12706  'ne'    northeast
12707  'hd'    horizontal drag
12708  'all'   all
12709 </pre>
12710  * <p>Here's an example showing the creation of a typical Resizable:</p>
12711  * <pre><code>
12712 var resizer = new Roo.Resizable("element-id", {
12713     handles: 'all',
12714     minWidth: 200,
12715     minHeight: 100,
12716     maxWidth: 500,
12717     maxHeight: 400,
12718     pinned: true
12719 });
12720 resizer.on("resize", myHandler);
12721 </code></pre>
12722  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12723  * resizer.east.setDisplayed(false);</p>
12724  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12725  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12726  * resize operation's new size (defaults to [0, 0])
12727  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12728  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12729  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12730  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12731  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12732  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12733  * @cfg {Number} width The width of the element in pixels (defaults to null)
12734  * @cfg {Number} height The height of the element in pixels (defaults to null)
12735  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12736  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12737  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12738  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12739  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12740  * in favor of the handles config option (defaults to false)
12741  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12742  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12743  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12744  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12745  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12746  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12747  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12748  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12749  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12750  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12751  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12752  * @constructor
12753  * Create a new resizable component
12754  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12755  * @param {Object} config configuration options
12756   */
12757 Roo.Resizable = function(el, config)
12758 {
12759     this.el = Roo.get(el);
12760
12761     if(config && config.wrap){
12762         config.resizeChild = this.el;
12763         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12764         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12765         this.el.setStyle("overflow", "hidden");
12766         this.el.setPositioning(config.resizeChild.getPositioning());
12767         config.resizeChild.clearPositioning();
12768         if(!config.width || !config.height){
12769             var csize = config.resizeChild.getSize();
12770             this.el.setSize(csize.width, csize.height);
12771         }
12772         if(config.pinned && !config.adjustments){
12773             config.adjustments = "auto";
12774         }
12775     }
12776
12777     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12778     this.proxy.unselectable();
12779     this.proxy.enableDisplayMode('block');
12780
12781     Roo.apply(this, config);
12782
12783     if(this.pinned){
12784         this.disableTrackOver = true;
12785         this.el.addClass("x-resizable-pinned");
12786     }
12787     // if the element isn't positioned, make it relative
12788     var position = this.el.getStyle("position");
12789     if(position != "absolute" && position != "fixed"){
12790         this.el.setStyle("position", "relative");
12791     }
12792     if(!this.handles){ // no handles passed, must be legacy style
12793         this.handles = 's,e,se';
12794         if(this.multiDirectional){
12795             this.handles += ',n,w';
12796         }
12797     }
12798     if(this.handles == "all"){
12799         this.handles = "n s e w ne nw se sw";
12800     }
12801     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12802     var ps = Roo.Resizable.positions;
12803     for(var i = 0, len = hs.length; i < len; i++){
12804         if(hs[i] && ps[hs[i]]){
12805             var pos = ps[hs[i]];
12806             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12807         }
12808     }
12809     // legacy
12810     this.corner = this.southeast;
12811     
12812     // updateBox = the box can move..
12813     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12814         this.updateBox = true;
12815     }
12816
12817     this.activeHandle = null;
12818
12819     if(this.resizeChild){
12820         if(typeof this.resizeChild == "boolean"){
12821             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12822         }else{
12823             this.resizeChild = Roo.get(this.resizeChild, true);
12824         }
12825     }
12826     
12827     if(this.adjustments == "auto"){
12828         var rc = this.resizeChild;
12829         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12830         if(rc && (hw || hn)){
12831             rc.position("relative");
12832             rc.setLeft(hw ? hw.el.getWidth() : 0);
12833             rc.setTop(hn ? hn.el.getHeight() : 0);
12834         }
12835         this.adjustments = [
12836             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12837             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12838         ];
12839     }
12840
12841     if(this.draggable){
12842         this.dd = this.dynamic ?
12843             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12844         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12845     }
12846
12847     // public events
12848     this.addEvents({
12849         /**
12850          * @event beforeresize
12851          * Fired before resize is allowed. Set enabled to false to cancel resize.
12852          * @param {Roo.Resizable} this
12853          * @param {Roo.EventObject} e The mousedown event
12854          */
12855         "beforeresize" : true,
12856         /**
12857          * @event resizing
12858          * Fired a resizing.
12859          * @param {Roo.Resizable} this
12860          * @param {Number} x The new x position
12861          * @param {Number} y The new y position
12862          * @param {Number} w The new w width
12863          * @param {Number} h The new h hight
12864          * @param {Roo.EventObject} e The mouseup event
12865          */
12866         "resizing" : true,
12867         /**
12868          * @event resize
12869          * Fired after a resize.
12870          * @param {Roo.Resizable} this
12871          * @param {Number} width The new width
12872          * @param {Number} height The new height
12873          * @param {Roo.EventObject} e The mouseup event
12874          */
12875         "resize" : true
12876     });
12877
12878     if(this.width !== null && this.height !== null){
12879         this.resizeTo(this.width, this.height);
12880     }else{
12881         this.updateChildSize();
12882     }
12883     if(Roo.isIE){
12884         this.el.dom.style.zoom = 1;
12885     }
12886     Roo.Resizable.superclass.constructor.call(this);
12887 };
12888
12889 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12890         resizeChild : false,
12891         adjustments : [0, 0],
12892         minWidth : 5,
12893         minHeight : 5,
12894         maxWidth : 10000,
12895         maxHeight : 10000,
12896         enabled : true,
12897         animate : false,
12898         duration : .35,
12899         dynamic : false,
12900         handles : false,
12901         multiDirectional : false,
12902         disableTrackOver : false,
12903         easing : 'easeOutStrong',
12904         widthIncrement : 0,
12905         heightIncrement : 0,
12906         pinned : false,
12907         width : null,
12908         height : null,
12909         preserveRatio : false,
12910         transparent: false,
12911         minX: 0,
12912         minY: 0,
12913         draggable: false,
12914
12915         /**
12916          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12917          */
12918         constrainTo: undefined,
12919         /**
12920          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12921          */
12922         resizeRegion: undefined,
12923
12924
12925     /**
12926      * Perform a manual resize
12927      * @param {Number} width
12928      * @param {Number} height
12929      */
12930     resizeTo : function(width, height){
12931         this.el.setSize(width, height);
12932         this.updateChildSize();
12933         this.fireEvent("resize", this, width, height, null);
12934     },
12935
12936     // private
12937     startSizing : function(e, handle){
12938         this.fireEvent("beforeresize", this, e);
12939         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12940
12941             if(!this.overlay){
12942                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12943                 this.overlay.unselectable();
12944                 this.overlay.enableDisplayMode("block");
12945                 this.overlay.on("mousemove", this.onMouseMove, this);
12946                 this.overlay.on("mouseup", this.onMouseUp, this);
12947             }
12948             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12949
12950             this.resizing = true;
12951             this.startBox = this.el.getBox();
12952             this.startPoint = e.getXY();
12953             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12954                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12955
12956             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12957             this.overlay.show();
12958
12959             if(this.constrainTo) {
12960                 var ct = Roo.get(this.constrainTo);
12961                 this.resizeRegion = ct.getRegion().adjust(
12962                     ct.getFrameWidth('t'),
12963                     ct.getFrameWidth('l'),
12964                     -ct.getFrameWidth('b'),
12965                     -ct.getFrameWidth('r')
12966                 );
12967             }
12968
12969             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
12970             this.proxy.show();
12971             this.proxy.setBox(this.startBox);
12972             if(!this.dynamic){
12973                 this.proxy.setStyle('visibility', 'visible');
12974             }
12975         }
12976     },
12977
12978     // private
12979     onMouseDown : function(handle, e){
12980         if(this.enabled){
12981             e.stopEvent();
12982             this.activeHandle = handle;
12983             this.startSizing(e, handle);
12984         }
12985     },
12986
12987     // private
12988     onMouseUp : function(e){
12989         var size = this.resizeElement();
12990         this.resizing = false;
12991         this.handleOut();
12992         this.overlay.hide();
12993         this.proxy.hide();
12994         this.fireEvent("resize", this, size.width, size.height, e);
12995     },
12996
12997     // private
12998     updateChildSize : function(){
12999         
13000         if(this.resizeChild){
13001             var el = this.el;
13002             var child = this.resizeChild;
13003             var adj = this.adjustments;
13004             if(el.dom.offsetWidth){
13005                 var b = el.getSize(true);
13006                 child.setSize(b.width+adj[0], b.height+adj[1]);
13007             }
13008             // Second call here for IE
13009             // The first call enables instant resizing and
13010             // the second call corrects scroll bars if they
13011             // exist
13012             if(Roo.isIE){
13013                 setTimeout(function(){
13014                     if(el.dom.offsetWidth){
13015                         var b = el.getSize(true);
13016                         child.setSize(b.width+adj[0], b.height+adj[1]);
13017                     }
13018                 }, 10);
13019             }
13020         }
13021     },
13022
13023     // private
13024     snap : function(value, inc, min){
13025         if(!inc || !value) return value;
13026         var newValue = value;
13027         var m = value % inc;
13028         if(m > 0){
13029             if(m > (inc/2)){
13030                 newValue = value + (inc-m);
13031             }else{
13032                 newValue = value - m;
13033             }
13034         }
13035         return Math.max(min, newValue);
13036     },
13037
13038     // private
13039     resizeElement : function(){
13040         var box = this.proxy.getBox();
13041         if(this.updateBox){
13042             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13043         }else{
13044             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13045         }
13046         this.updateChildSize();
13047         if(!this.dynamic){
13048             this.proxy.hide();
13049         }
13050         return box;
13051     },
13052
13053     // private
13054     constrain : function(v, diff, m, mx){
13055         if(v - diff < m){
13056             diff = v - m;
13057         }else if(v - diff > mx){
13058             diff = mx - v;
13059         }
13060         return diff;
13061     },
13062
13063     // private
13064     onMouseMove : function(e){
13065         
13066         if(this.enabled){
13067             try{// try catch so if something goes wrong the user doesn't get hung
13068
13069             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13070                 return;
13071             }
13072
13073             //var curXY = this.startPoint;
13074             var curSize = this.curSize || this.startBox;
13075             var x = this.startBox.x, y = this.startBox.y;
13076             var ox = x, oy = y;
13077             var w = curSize.width, h = curSize.height;
13078             var ow = w, oh = h;
13079             var mw = this.minWidth, mh = this.minHeight;
13080             var mxw = this.maxWidth, mxh = this.maxHeight;
13081             var wi = this.widthIncrement;
13082             var hi = this.heightIncrement;
13083
13084             var eventXY = e.getXY();
13085             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13086             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13087
13088             var pos = this.activeHandle.position;
13089
13090             switch(pos){
13091                 case "east":
13092                     w += diffX;
13093                     w = Math.min(Math.max(mw, w), mxw);
13094                     break;
13095              
13096                 case "south":
13097                     h += diffY;
13098                     h = Math.min(Math.max(mh, h), mxh);
13099                     break;
13100                 case "southeast":
13101                     w += diffX;
13102                     h += diffY;
13103                     w = Math.min(Math.max(mw, w), mxw);
13104                     h = Math.min(Math.max(mh, h), mxh);
13105                     break;
13106                 case "north":
13107                     diffY = this.constrain(h, diffY, mh, mxh);
13108                     y += diffY;
13109                     h -= diffY;
13110                     break;
13111                 case "hdrag":
13112                     
13113                     if (wi) {
13114                         var adiffX = Math.abs(diffX);
13115                         var sub = (adiffX % wi); // how much 
13116                         if (sub > (wi/2)) { // far enough to snap
13117                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13118                         } else {
13119                             // remove difference.. 
13120                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13121                         }
13122                     }
13123                     x += diffX;
13124                     x = Math.max(this.minX, x);
13125                     break;
13126                 case "west":
13127                     diffX = this.constrain(w, diffX, mw, mxw);
13128                     x += diffX;
13129                     w -= diffX;
13130                     break;
13131                 case "northeast":
13132                     w += diffX;
13133                     w = Math.min(Math.max(mw, w), mxw);
13134                     diffY = this.constrain(h, diffY, mh, mxh);
13135                     y += diffY;
13136                     h -= diffY;
13137                     break;
13138                 case "northwest":
13139                     diffX = this.constrain(w, diffX, mw, mxw);
13140                     diffY = this.constrain(h, diffY, mh, mxh);
13141                     y += diffY;
13142                     h -= diffY;
13143                     x += diffX;
13144                     w -= diffX;
13145                     break;
13146                case "southwest":
13147                     diffX = this.constrain(w, diffX, mw, mxw);
13148                     h += diffY;
13149                     h = Math.min(Math.max(mh, h), mxh);
13150                     x += diffX;
13151                     w -= diffX;
13152                     break;
13153             }
13154
13155             var sw = this.snap(w, wi, mw);
13156             var sh = this.snap(h, hi, mh);
13157             if(sw != w || sh != h){
13158                 switch(pos){
13159                     case "northeast":
13160                         y -= sh - h;
13161                     break;
13162                     case "north":
13163                         y -= sh - h;
13164                         break;
13165                     case "southwest":
13166                         x -= sw - w;
13167                     break;
13168                     case "west":
13169                         x -= sw - w;
13170                         break;
13171                     case "northwest":
13172                         x -= sw - w;
13173                         y -= sh - h;
13174                     break;
13175                 }
13176                 w = sw;
13177                 h = sh;
13178             }
13179
13180             if(this.preserveRatio){
13181                 switch(pos){
13182                     case "southeast":
13183                     case "east":
13184                         h = oh * (w/ow);
13185                         h = Math.min(Math.max(mh, h), mxh);
13186                         w = ow * (h/oh);
13187                        break;
13188                     case "south":
13189                         w = ow * (h/oh);
13190                         w = Math.min(Math.max(mw, w), mxw);
13191                         h = oh * (w/ow);
13192                         break;
13193                     case "northeast":
13194                         w = ow * (h/oh);
13195                         w = Math.min(Math.max(mw, w), mxw);
13196                         h = oh * (w/ow);
13197                     break;
13198                     case "north":
13199                         var tw = w;
13200                         w = ow * (h/oh);
13201                         w = Math.min(Math.max(mw, w), mxw);
13202                         h = oh * (w/ow);
13203                         x += (tw - w) / 2;
13204                         break;
13205                     case "southwest":
13206                         h = oh * (w/ow);
13207                         h = Math.min(Math.max(mh, h), mxh);
13208                         var tw = w;
13209                         w = ow * (h/oh);
13210                         x += tw - w;
13211                         break;
13212                     case "west":
13213                         var th = h;
13214                         h = oh * (w/ow);
13215                         h = Math.min(Math.max(mh, h), mxh);
13216                         y += (th - h) / 2;
13217                         var tw = w;
13218                         w = ow * (h/oh);
13219                         x += tw - w;
13220                        break;
13221                     case "northwest":
13222                         var tw = w;
13223                         var th = h;
13224                         h = oh * (w/ow);
13225                         h = Math.min(Math.max(mh, h), mxh);
13226                         w = ow * (h/oh);
13227                         y += th - h;
13228                         x += tw - w;
13229                        break;
13230
13231                 }
13232             }
13233             if (pos == 'hdrag') {
13234                 w = ow;
13235             }
13236             this.proxy.setBounds(x, y, w, h);
13237             if(this.dynamic){
13238                 this.resizeElement();
13239             }
13240             }catch(e){}
13241         }
13242         this.fireEvent("resizing", this, x, y, w, h, e);
13243     },
13244
13245     // private
13246     handleOver : function(){
13247         if(this.enabled){
13248             this.el.addClass("x-resizable-over");
13249         }
13250     },
13251
13252     // private
13253     handleOut : function(){
13254         if(!this.resizing){
13255             this.el.removeClass("x-resizable-over");
13256         }
13257     },
13258
13259     /**
13260      * Returns the element this component is bound to.
13261      * @return {Roo.Element}
13262      */
13263     getEl : function(){
13264         return this.el;
13265     },
13266
13267     /**
13268      * Returns the resizeChild element (or null).
13269      * @return {Roo.Element}
13270      */
13271     getResizeChild : function(){
13272         return this.resizeChild;
13273     },
13274     groupHandler : function()
13275     {
13276         
13277     },
13278     /**
13279      * Destroys this resizable. If the element was wrapped and
13280      * removeEl is not true then the element remains.
13281      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13282      */
13283     destroy : function(removeEl){
13284         this.proxy.remove();
13285         if(this.overlay){
13286             this.overlay.removeAllListeners();
13287             this.overlay.remove();
13288         }
13289         var ps = Roo.Resizable.positions;
13290         for(var k in ps){
13291             if(typeof ps[k] != "function" && this[ps[k]]){
13292                 var h = this[ps[k]];
13293                 h.el.removeAllListeners();
13294                 h.el.remove();
13295             }
13296         }
13297         if(removeEl){
13298             this.el.update("");
13299             this.el.remove();
13300         }
13301     }
13302 });
13303
13304 // private
13305 // hash to map config positions to true positions
13306 Roo.Resizable.positions = {
13307     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13308     hd: "hdrag"
13309 };
13310
13311 // private
13312 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13313     if(!this.tpl){
13314         // only initialize the template if resizable is used
13315         var tpl = Roo.DomHelper.createTemplate(
13316             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13317         );
13318         tpl.compile();
13319         Roo.Resizable.Handle.prototype.tpl = tpl;
13320     }
13321     this.position = pos;
13322     this.rz = rz;
13323     // show north drag fro topdra
13324     var handlepos = pos == 'hdrag' ? 'north' : pos;
13325     
13326     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13327     if (pos == 'hdrag') {
13328         this.el.setStyle('cursor', 'pointer');
13329     }
13330     this.el.unselectable();
13331     if(transparent){
13332         this.el.setOpacity(0);
13333     }
13334     this.el.on("mousedown", this.onMouseDown, this);
13335     if(!disableTrackOver){
13336         this.el.on("mouseover", this.onMouseOver, this);
13337         this.el.on("mouseout", this.onMouseOut, this);
13338     }
13339 };
13340
13341 // private
13342 Roo.Resizable.Handle.prototype = {
13343     afterResize : function(rz){
13344         Roo.log('after?');
13345         // do nothing
13346     },
13347     // private
13348     onMouseDown : function(e){
13349         this.rz.onMouseDown(this, e);
13350     },
13351     // private
13352     onMouseOver : function(e){
13353         this.rz.handleOver(this, e);
13354     },
13355     // private
13356     onMouseOut : function(e){
13357         this.rz.handleOut(this, e);
13358     }
13359 };/*
13360  * Based on:
13361  * Ext JS Library 1.1.1
13362  * Copyright(c) 2006-2007, Ext JS, LLC.
13363  *
13364  * Originally Released Under LGPL - original licence link has changed is not relivant.
13365  *
13366  * Fork - LGPL
13367  * <script type="text/javascript">
13368  */
13369
13370 /**
13371  * @class Roo.Editor
13372  * @extends Roo.Component
13373  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13374  * @constructor
13375  * Create a new Editor
13376  * @param {Roo.form.Field} field The Field object (or descendant)
13377  * @param {Object} config The config object
13378  */
13379 Roo.Editor = function(field, config){
13380     Roo.Editor.superclass.constructor.call(this, config);
13381     this.field = field;
13382     this.addEvents({
13383         /**
13384              * @event beforestartedit
13385              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13386              * false from the handler of this event.
13387              * @param {Editor} this
13388              * @param {Roo.Element} boundEl The underlying element bound to this editor
13389              * @param {Mixed} value The field value being set
13390              */
13391         "beforestartedit" : true,
13392         /**
13393              * @event startedit
13394              * Fires when this editor is displayed
13395              * @param {Roo.Element} boundEl The underlying element bound to this editor
13396              * @param {Mixed} value The starting field value
13397              */
13398         "startedit" : true,
13399         /**
13400              * @event beforecomplete
13401              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13402              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13403              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13404              * event will not fire since no edit actually occurred.
13405              * @param {Editor} this
13406              * @param {Mixed} value The current field value
13407              * @param {Mixed} startValue The original field value
13408              */
13409         "beforecomplete" : true,
13410         /**
13411              * @event complete
13412              * Fires after editing is complete and any changed value has been written to the underlying field.
13413              * @param {Editor} this
13414              * @param {Mixed} value The current field value
13415              * @param {Mixed} startValue The original field value
13416              */
13417         "complete" : true,
13418         /**
13419          * @event specialkey
13420          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13421          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13422          * @param {Roo.form.Field} this
13423          * @param {Roo.EventObject} e The event object
13424          */
13425         "specialkey" : true
13426     });
13427 };
13428
13429 Roo.extend(Roo.Editor, Roo.Component, {
13430     /**
13431      * @cfg {Boolean/String} autosize
13432      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13433      * or "height" to adopt the height only (defaults to false)
13434      */
13435     /**
13436      * @cfg {Boolean} revertInvalid
13437      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13438      * validation fails (defaults to true)
13439      */
13440     /**
13441      * @cfg {Boolean} ignoreNoChange
13442      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13443      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13444      * will never be ignored.
13445      */
13446     /**
13447      * @cfg {Boolean} hideEl
13448      * False to keep the bound element visible while the editor is displayed (defaults to true)
13449      */
13450     /**
13451      * @cfg {Mixed} value
13452      * The data value of the underlying field (defaults to "")
13453      */
13454     value : "",
13455     /**
13456      * @cfg {String} alignment
13457      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13458      */
13459     alignment: "c-c?",
13460     /**
13461      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13462      * for bottom-right shadow (defaults to "frame")
13463      */
13464     shadow : "frame",
13465     /**
13466      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13467      */
13468     constrain : false,
13469     /**
13470      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13471      */
13472     completeOnEnter : false,
13473     /**
13474      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13475      */
13476     cancelOnEsc : false,
13477     /**
13478      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13479      */
13480     updateEl : false,
13481
13482     // private
13483     onRender : function(ct, position){
13484         this.el = new Roo.Layer({
13485             shadow: this.shadow,
13486             cls: "x-editor",
13487             parentEl : ct,
13488             shim : this.shim,
13489             shadowOffset:4,
13490             id: this.id,
13491             constrain: this.constrain
13492         });
13493         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13494         if(this.field.msgTarget != 'title'){
13495             this.field.msgTarget = 'qtip';
13496         }
13497         this.field.render(this.el);
13498         if(Roo.isGecko){
13499             this.field.el.dom.setAttribute('autocomplete', 'off');
13500         }
13501         this.field.on("specialkey", this.onSpecialKey, this);
13502         if(this.swallowKeys){
13503             this.field.el.swallowEvent(['keydown','keypress']);
13504         }
13505         this.field.show();
13506         this.field.on("blur", this.onBlur, this);
13507         if(this.field.grow){
13508             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13509         }
13510     },
13511
13512     onSpecialKey : function(field, e)
13513     {
13514         //Roo.log('editor onSpecialKey');
13515         if(this.completeOnEnter && e.getKey() == e.ENTER){
13516             e.stopEvent();
13517             this.completeEdit();
13518             return;
13519         }
13520         // do not fire special key otherwise it might hide close the editor...
13521         if(e.getKey() == e.ENTER){    
13522             return;
13523         }
13524         if(this.cancelOnEsc && e.getKey() == e.ESC){
13525             this.cancelEdit();
13526             return;
13527         } 
13528         this.fireEvent('specialkey', field, e);
13529     
13530     },
13531
13532     /**
13533      * Starts the editing process and shows the editor.
13534      * @param {String/HTMLElement/Element} el The element to edit
13535      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13536       * to the innerHTML of el.
13537      */
13538     startEdit : function(el, value){
13539         if(this.editing){
13540             this.completeEdit();
13541         }
13542         this.boundEl = Roo.get(el);
13543         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13544         if(!this.rendered){
13545             this.render(this.parentEl || document.body);
13546         }
13547         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13548             return;
13549         }
13550         this.startValue = v;
13551         this.field.setValue(v);
13552         if(this.autoSize){
13553             var sz = this.boundEl.getSize();
13554             switch(this.autoSize){
13555                 case "width":
13556                 this.setSize(sz.width,  "");
13557                 break;
13558                 case "height":
13559                 this.setSize("",  sz.height);
13560                 break;
13561                 default:
13562                 this.setSize(sz.width,  sz.height);
13563             }
13564         }
13565         this.el.alignTo(this.boundEl, this.alignment);
13566         this.editing = true;
13567         if(Roo.QuickTips){
13568             Roo.QuickTips.disable();
13569         }
13570         this.show();
13571     },
13572
13573     /**
13574      * Sets the height and width of this editor.
13575      * @param {Number} width The new width
13576      * @param {Number} height The new height
13577      */
13578     setSize : function(w, h){
13579         this.field.setSize(w, h);
13580         if(this.el){
13581             this.el.sync();
13582         }
13583     },
13584
13585     /**
13586      * Realigns the editor to the bound field based on the current alignment config value.
13587      */
13588     realign : function(){
13589         this.el.alignTo(this.boundEl, this.alignment);
13590     },
13591
13592     /**
13593      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13594      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13595      */
13596     completeEdit : function(remainVisible){
13597         if(!this.editing){
13598             return;
13599         }
13600         var v = this.getValue();
13601         if(this.revertInvalid !== false && !this.field.isValid()){
13602             v = this.startValue;
13603             this.cancelEdit(true);
13604         }
13605         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13606             this.editing = false;
13607             this.hide();
13608             return;
13609         }
13610         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13611             this.editing = false;
13612             if(this.updateEl && this.boundEl){
13613                 this.boundEl.update(v);
13614             }
13615             if(remainVisible !== true){
13616                 this.hide();
13617             }
13618             this.fireEvent("complete", this, v, this.startValue);
13619         }
13620     },
13621
13622     // private
13623     onShow : function(){
13624         this.el.show();
13625         if(this.hideEl !== false){
13626             this.boundEl.hide();
13627         }
13628         this.field.show();
13629         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13630             this.fixIEFocus = true;
13631             this.deferredFocus.defer(50, this);
13632         }else{
13633             this.field.focus();
13634         }
13635         this.fireEvent("startedit", this.boundEl, this.startValue);
13636     },
13637
13638     deferredFocus : function(){
13639         if(this.editing){
13640             this.field.focus();
13641         }
13642     },
13643
13644     /**
13645      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13646      * reverted to the original starting value.
13647      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13648      * cancel (defaults to false)
13649      */
13650     cancelEdit : function(remainVisible){
13651         if(this.editing){
13652             this.setValue(this.startValue);
13653             if(remainVisible !== true){
13654                 this.hide();
13655             }
13656         }
13657     },
13658
13659     // private
13660     onBlur : function(){
13661         if(this.allowBlur !== true && this.editing){
13662             this.completeEdit();
13663         }
13664     },
13665
13666     // private
13667     onHide : function(){
13668         if(this.editing){
13669             this.completeEdit();
13670             return;
13671         }
13672         this.field.blur();
13673         if(this.field.collapse){
13674             this.field.collapse();
13675         }
13676         this.el.hide();
13677         if(this.hideEl !== false){
13678             this.boundEl.show();
13679         }
13680         if(Roo.QuickTips){
13681             Roo.QuickTips.enable();
13682         }
13683     },
13684
13685     /**
13686      * Sets the data value of the editor
13687      * @param {Mixed} value Any valid value supported by the underlying field
13688      */
13689     setValue : function(v){
13690         this.field.setValue(v);
13691     },
13692
13693     /**
13694      * Gets the data value of the editor
13695      * @return {Mixed} The data value
13696      */
13697     getValue : function(){
13698         return this.field.getValue();
13699     }
13700 });/*
13701  * Based on:
13702  * Ext JS Library 1.1.1
13703  * Copyright(c) 2006-2007, Ext JS, LLC.
13704  *
13705  * Originally Released Under LGPL - original licence link has changed is not relivant.
13706  *
13707  * Fork - LGPL
13708  * <script type="text/javascript">
13709  */
13710  
13711 /**
13712  * @class Roo.BasicDialog
13713  * @extends Roo.util.Observable
13714  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13715  * <pre><code>
13716 var dlg = new Roo.BasicDialog("my-dlg", {
13717     height: 200,
13718     width: 300,
13719     minHeight: 100,
13720     minWidth: 150,
13721     modal: true,
13722     proxyDrag: true,
13723     shadow: true
13724 });
13725 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13726 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13727 dlg.addButton('Cancel', dlg.hide, dlg);
13728 dlg.show();
13729 </code></pre>
13730   <b>A Dialog should always be a direct child of the body element.</b>
13731  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13732  * @cfg {String} title Default text to display in the title bar (defaults to null)
13733  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13734  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13735  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13736  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13737  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13738  * (defaults to null with no animation)
13739  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13740  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13741  * property for valid values (defaults to 'all')
13742  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13743  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13744  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13745  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13746  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13747  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13748  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13749  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13750  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13751  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13752  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13753  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13754  * draggable = true (defaults to false)
13755  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13756  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13757  * shadow (defaults to false)
13758  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13759  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13760  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13761  * @cfg {Array} buttons Array of buttons
13762  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13763  * @constructor
13764  * Create a new BasicDialog.
13765  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13766  * @param {Object} config Configuration options
13767  */
13768 Roo.BasicDialog = function(el, config){
13769     this.el = Roo.get(el);
13770     var dh = Roo.DomHelper;
13771     if(!this.el && config && config.autoCreate){
13772         if(typeof config.autoCreate == "object"){
13773             if(!config.autoCreate.id){
13774                 config.autoCreate.id = el;
13775             }
13776             this.el = dh.append(document.body,
13777                         config.autoCreate, true);
13778         }else{
13779             this.el = dh.append(document.body,
13780                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13781         }
13782     }
13783     el = this.el;
13784     el.setDisplayed(true);
13785     el.hide = this.hideAction;
13786     this.id = el.id;
13787     el.addClass("x-dlg");
13788
13789     Roo.apply(this, config);
13790
13791     this.proxy = el.createProxy("x-dlg-proxy");
13792     this.proxy.hide = this.hideAction;
13793     this.proxy.setOpacity(.5);
13794     this.proxy.hide();
13795
13796     if(config.width){
13797         el.setWidth(config.width);
13798     }
13799     if(config.height){
13800         el.setHeight(config.height);
13801     }
13802     this.size = el.getSize();
13803     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13804         this.xy = [config.x,config.y];
13805     }else{
13806         this.xy = el.getCenterXY(true);
13807     }
13808     /** The header element @type Roo.Element */
13809     this.header = el.child("> .x-dlg-hd");
13810     /** The body element @type Roo.Element */
13811     this.body = el.child("> .x-dlg-bd");
13812     /** The footer element @type Roo.Element */
13813     this.footer = el.child("> .x-dlg-ft");
13814
13815     if(!this.header){
13816         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13817     }
13818     if(!this.body){
13819         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13820     }
13821
13822     this.header.unselectable();
13823     if(this.title){
13824         this.header.update(this.title);
13825     }
13826     // this element allows the dialog to be focused for keyboard event
13827     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13828     this.focusEl.swallowEvent("click", true);
13829
13830     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13831
13832     // wrap the body and footer for special rendering
13833     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13834     if(this.footer){
13835         this.bwrap.dom.appendChild(this.footer.dom);
13836     }
13837
13838     this.bg = this.el.createChild({
13839         tag: "div", cls:"x-dlg-bg",
13840         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13841     });
13842     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13843
13844
13845     if(this.autoScroll !== false && !this.autoTabs){
13846         this.body.setStyle("overflow", "auto");
13847     }
13848
13849     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13850
13851     if(this.closable !== false){
13852         this.el.addClass("x-dlg-closable");
13853         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13854         this.close.on("click", this.closeClick, this);
13855         this.close.addClassOnOver("x-dlg-close-over");
13856     }
13857     if(this.collapsible !== false){
13858         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13859         this.collapseBtn.on("click", this.collapseClick, this);
13860         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13861         this.header.on("dblclick", this.collapseClick, this);
13862     }
13863     if(this.resizable !== false){
13864         this.el.addClass("x-dlg-resizable");
13865         this.resizer = new Roo.Resizable(el, {
13866             minWidth: this.minWidth || 80,
13867             minHeight:this.minHeight || 80,
13868             handles: this.resizeHandles || "all",
13869             pinned: true
13870         });
13871         this.resizer.on("beforeresize", this.beforeResize, this);
13872         this.resizer.on("resize", this.onResize, this);
13873     }
13874     if(this.draggable !== false){
13875         el.addClass("x-dlg-draggable");
13876         if (!this.proxyDrag) {
13877             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13878         }
13879         else {
13880             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13881         }
13882         dd.setHandleElId(this.header.id);
13883         dd.endDrag = this.endMove.createDelegate(this);
13884         dd.startDrag = this.startMove.createDelegate(this);
13885         dd.onDrag = this.onDrag.createDelegate(this);
13886         dd.scroll = false;
13887         this.dd = dd;
13888     }
13889     if(this.modal){
13890         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13891         this.mask.enableDisplayMode("block");
13892         this.mask.hide();
13893         this.el.addClass("x-dlg-modal");
13894     }
13895     if(this.shadow){
13896         this.shadow = new Roo.Shadow({
13897             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13898             offset : this.shadowOffset
13899         });
13900     }else{
13901         this.shadowOffset = 0;
13902     }
13903     if(Roo.useShims && this.shim !== false){
13904         this.shim = this.el.createShim();
13905         this.shim.hide = this.hideAction;
13906         this.shim.hide();
13907     }else{
13908         this.shim = false;
13909     }
13910     if(this.autoTabs){
13911         this.initTabs();
13912     }
13913     if (this.buttons) { 
13914         var bts= this.buttons;
13915         this.buttons = [];
13916         Roo.each(bts, function(b) {
13917             this.addButton(b);
13918         }, this);
13919     }
13920     
13921     
13922     this.addEvents({
13923         /**
13924          * @event keydown
13925          * Fires when a key is pressed
13926          * @param {Roo.BasicDialog} this
13927          * @param {Roo.EventObject} e
13928          */
13929         "keydown" : true,
13930         /**
13931          * @event move
13932          * Fires when this dialog is moved by the user.
13933          * @param {Roo.BasicDialog} this
13934          * @param {Number} x The new page X
13935          * @param {Number} y The new page Y
13936          */
13937         "move" : true,
13938         /**
13939          * @event resize
13940          * Fires when this dialog is resized by the user.
13941          * @param {Roo.BasicDialog} this
13942          * @param {Number} width The new width
13943          * @param {Number} height The new height
13944          */
13945         "resize" : true,
13946         /**
13947          * @event beforehide
13948          * Fires before this dialog is hidden.
13949          * @param {Roo.BasicDialog} this
13950          */
13951         "beforehide" : true,
13952         /**
13953          * @event hide
13954          * Fires when this dialog is hidden.
13955          * @param {Roo.BasicDialog} this
13956          */
13957         "hide" : true,
13958         /**
13959          * @event beforeshow
13960          * Fires before this dialog is shown.
13961          * @param {Roo.BasicDialog} this
13962          */
13963         "beforeshow" : true,
13964         /**
13965          * @event show
13966          * Fires when this dialog is shown.
13967          * @param {Roo.BasicDialog} this
13968          */
13969         "show" : true
13970     });
13971     el.on("keydown", this.onKeyDown, this);
13972     el.on("mousedown", this.toFront, this);
13973     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
13974     this.el.hide();
13975     Roo.DialogManager.register(this);
13976     Roo.BasicDialog.superclass.constructor.call(this);
13977 };
13978
13979 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
13980     shadowOffset: Roo.isIE ? 6 : 5,
13981     minHeight: 80,
13982     minWidth: 200,
13983     minButtonWidth: 75,
13984     defaultButton: null,
13985     buttonAlign: "right",
13986     tabTag: 'div',
13987     firstShow: true,
13988
13989     /**
13990      * Sets the dialog title text
13991      * @param {String} text The title text to display
13992      * @return {Roo.BasicDialog} this
13993      */
13994     setTitle : function(text){
13995         this.header.update(text);
13996         return this;
13997     },
13998
13999     // private
14000     closeClick : function(){
14001         this.hide();
14002     },
14003
14004     // private
14005     collapseClick : function(){
14006         this[this.collapsed ? "expand" : "collapse"]();
14007     },
14008
14009     /**
14010      * Collapses the dialog to its minimized state (only the title bar is visible).
14011      * Equivalent to the user clicking the collapse dialog button.
14012      */
14013     collapse : function(){
14014         if(!this.collapsed){
14015             this.collapsed = true;
14016             this.el.addClass("x-dlg-collapsed");
14017             this.restoreHeight = this.el.getHeight();
14018             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14019         }
14020     },
14021
14022     /**
14023      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14024      * clicking the expand dialog button.
14025      */
14026     expand : function(){
14027         if(this.collapsed){
14028             this.collapsed = false;
14029             this.el.removeClass("x-dlg-collapsed");
14030             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14031         }
14032     },
14033
14034     /**
14035      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14036      * @return {Roo.TabPanel} The tabs component
14037      */
14038     initTabs : function(){
14039         var tabs = this.getTabs();
14040         while(tabs.getTab(0)){
14041             tabs.removeTab(0);
14042         }
14043         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14044             var dom = el.dom;
14045             tabs.addTab(Roo.id(dom), dom.title);
14046             dom.title = "";
14047         });
14048         tabs.activate(0);
14049         return tabs;
14050     },
14051
14052     // private
14053     beforeResize : function(){
14054         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14055     },
14056
14057     // private
14058     onResize : function(){
14059         this.refreshSize();
14060         this.syncBodyHeight();
14061         this.adjustAssets();
14062         this.focus();
14063         this.fireEvent("resize", this, this.size.width, this.size.height);
14064     },
14065
14066     // private
14067     onKeyDown : function(e){
14068         if(this.isVisible()){
14069             this.fireEvent("keydown", this, e);
14070         }
14071     },
14072
14073     /**
14074      * Resizes the dialog.
14075      * @param {Number} width
14076      * @param {Number} height
14077      * @return {Roo.BasicDialog} this
14078      */
14079     resizeTo : function(width, height){
14080         this.el.setSize(width, height);
14081         this.size = {width: width, height: height};
14082         this.syncBodyHeight();
14083         if(this.fixedcenter){
14084             this.center();
14085         }
14086         if(this.isVisible()){
14087             this.constrainXY();
14088             this.adjustAssets();
14089         }
14090         this.fireEvent("resize", this, width, height);
14091         return this;
14092     },
14093
14094
14095     /**
14096      * Resizes the dialog to fit the specified content size.
14097      * @param {Number} width
14098      * @param {Number} height
14099      * @return {Roo.BasicDialog} this
14100      */
14101     setContentSize : function(w, h){
14102         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14103         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14104         //if(!this.el.isBorderBox()){
14105             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14106             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14107         //}
14108         if(this.tabs){
14109             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14110             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14111         }
14112         this.resizeTo(w, h);
14113         return this;
14114     },
14115
14116     /**
14117      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14118      * executed in response to a particular key being pressed while the dialog is active.
14119      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14120      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14121      * @param {Function} fn The function to call
14122      * @param {Object} scope (optional) The scope of the function
14123      * @return {Roo.BasicDialog} this
14124      */
14125     addKeyListener : function(key, fn, scope){
14126         var keyCode, shift, ctrl, alt;
14127         if(typeof key == "object" && !(key instanceof Array)){
14128             keyCode = key["key"];
14129             shift = key["shift"];
14130             ctrl = key["ctrl"];
14131             alt = key["alt"];
14132         }else{
14133             keyCode = key;
14134         }
14135         var handler = function(dlg, e){
14136             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14137                 var k = e.getKey();
14138                 if(keyCode instanceof Array){
14139                     for(var i = 0, len = keyCode.length; i < len; i++){
14140                         if(keyCode[i] == k){
14141                           fn.call(scope || window, dlg, k, e);
14142                           return;
14143                         }
14144                     }
14145                 }else{
14146                     if(k == keyCode){
14147                         fn.call(scope || window, dlg, k, e);
14148                     }
14149                 }
14150             }
14151         };
14152         this.on("keydown", handler);
14153         return this;
14154     },
14155
14156     /**
14157      * Returns the TabPanel component (creates it if it doesn't exist).
14158      * Note: If you wish to simply check for the existence of tabs without creating them,
14159      * check for a null 'tabs' property.
14160      * @return {Roo.TabPanel} The tabs component
14161      */
14162     getTabs : function(){
14163         if(!this.tabs){
14164             this.el.addClass("x-dlg-auto-tabs");
14165             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14166             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14167         }
14168         return this.tabs;
14169     },
14170
14171     /**
14172      * Adds a button to the footer section of the dialog.
14173      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14174      * object or a valid Roo.DomHelper element config
14175      * @param {Function} handler The function called when the button is clicked
14176      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14177      * @return {Roo.Button} The new button
14178      */
14179     addButton : function(config, handler, scope){
14180         var dh = Roo.DomHelper;
14181         if(!this.footer){
14182             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14183         }
14184         if(!this.btnContainer){
14185             var tb = this.footer.createChild({
14186
14187                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14188                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14189             }, null, true);
14190             this.btnContainer = tb.firstChild.firstChild.firstChild;
14191         }
14192         var bconfig = {
14193             handler: handler,
14194             scope: scope,
14195             minWidth: this.minButtonWidth,
14196             hideParent:true
14197         };
14198         if(typeof config == "string"){
14199             bconfig.text = config;
14200         }else{
14201             if(config.tag){
14202                 bconfig.dhconfig = config;
14203             }else{
14204                 Roo.apply(bconfig, config);
14205             }
14206         }
14207         var fc = false;
14208         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14209             bconfig.position = Math.max(0, bconfig.position);
14210             fc = this.btnContainer.childNodes[bconfig.position];
14211         }
14212          
14213         var btn = new Roo.Button(
14214             fc ? 
14215                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14216                 : this.btnContainer.appendChild(document.createElement("td")),
14217             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14218             bconfig
14219         );
14220         this.syncBodyHeight();
14221         if(!this.buttons){
14222             /**
14223              * Array of all the buttons that have been added to this dialog via addButton
14224              * @type Array
14225              */
14226             this.buttons = [];
14227         }
14228         this.buttons.push(btn);
14229         return btn;
14230     },
14231
14232     /**
14233      * Sets the default button to be focused when the dialog is displayed.
14234      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14235      * @return {Roo.BasicDialog} this
14236      */
14237     setDefaultButton : function(btn){
14238         this.defaultButton = btn;
14239         return this;
14240     },
14241
14242     // private
14243     getHeaderFooterHeight : function(safe){
14244         var height = 0;
14245         if(this.header){
14246            height += this.header.getHeight();
14247         }
14248         if(this.footer){
14249            var fm = this.footer.getMargins();
14250             height += (this.footer.getHeight()+fm.top+fm.bottom);
14251         }
14252         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14253         height += this.centerBg.getPadding("tb");
14254         return height;
14255     },
14256
14257     // private
14258     syncBodyHeight : function()
14259     {
14260         var bd = this.body, // the text
14261             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14262             bw = this.bwrap;
14263         var height = this.size.height - this.getHeaderFooterHeight(false);
14264         bd.setHeight(height-bd.getMargins("tb"));
14265         var hh = this.header.getHeight();
14266         var h = this.size.height-hh;
14267         cb.setHeight(h);
14268         
14269         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14270         bw.setHeight(h-cb.getPadding("tb"));
14271         
14272         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14273         bd.setWidth(bw.getWidth(true));
14274         if(this.tabs){
14275             this.tabs.syncHeight();
14276             if(Roo.isIE){
14277                 this.tabs.el.repaint();
14278             }
14279         }
14280     },
14281
14282     /**
14283      * Restores the previous state of the dialog if Roo.state is configured.
14284      * @return {Roo.BasicDialog} this
14285      */
14286     restoreState : function(){
14287         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14288         if(box && box.width){
14289             this.xy = [box.x, box.y];
14290             this.resizeTo(box.width, box.height);
14291         }
14292         return this;
14293     },
14294
14295     // private
14296     beforeShow : function(){
14297         this.expand();
14298         if(this.fixedcenter){
14299             this.xy = this.el.getCenterXY(true);
14300         }
14301         if(this.modal){
14302             Roo.get(document.body).addClass("x-body-masked");
14303             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14304             this.mask.show();
14305         }
14306         this.constrainXY();
14307     },
14308
14309     // private
14310     animShow : function(){
14311         var b = Roo.get(this.animateTarget).getBox();
14312         this.proxy.setSize(b.width, b.height);
14313         this.proxy.setLocation(b.x, b.y);
14314         this.proxy.show();
14315         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14316                     true, .35, this.showEl.createDelegate(this));
14317     },
14318
14319     /**
14320      * Shows the dialog.
14321      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14322      * @return {Roo.BasicDialog} this
14323      */
14324     show : function(animateTarget){
14325         if (this.fireEvent("beforeshow", this) === false){
14326             return;
14327         }
14328         if(this.syncHeightBeforeShow){
14329             this.syncBodyHeight();
14330         }else if(this.firstShow){
14331             this.firstShow = false;
14332             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14333         }
14334         this.animateTarget = animateTarget || this.animateTarget;
14335         if(!this.el.isVisible()){
14336             this.beforeShow();
14337             if(this.animateTarget && Roo.get(this.animateTarget)){
14338                 this.animShow();
14339             }else{
14340                 this.showEl();
14341             }
14342         }
14343         return this;
14344     },
14345
14346     // private
14347     showEl : function(){
14348         this.proxy.hide();
14349         this.el.setXY(this.xy);
14350         this.el.show();
14351         this.adjustAssets(true);
14352         this.toFront();
14353         this.focus();
14354         // IE peekaboo bug - fix found by Dave Fenwick
14355         if(Roo.isIE){
14356             this.el.repaint();
14357         }
14358         this.fireEvent("show", this);
14359     },
14360
14361     /**
14362      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14363      * dialog itself will receive focus.
14364      */
14365     focus : function(){
14366         if(this.defaultButton){
14367             this.defaultButton.focus();
14368         }else{
14369             this.focusEl.focus();
14370         }
14371     },
14372
14373     // private
14374     constrainXY : function(){
14375         if(this.constraintoviewport !== false){
14376             if(!this.viewSize){
14377                 if(this.container){
14378                     var s = this.container.getSize();
14379                     this.viewSize = [s.width, s.height];
14380                 }else{
14381                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14382                 }
14383             }
14384             var s = Roo.get(this.container||document).getScroll();
14385
14386             var x = this.xy[0], y = this.xy[1];
14387             var w = this.size.width, h = this.size.height;
14388             var vw = this.viewSize[0], vh = this.viewSize[1];
14389             // only move it if it needs it
14390             var moved = false;
14391             // first validate right/bottom
14392             if(x + w > vw+s.left){
14393                 x = vw - w;
14394                 moved = true;
14395             }
14396             if(y + h > vh+s.top){
14397                 y = vh - h;
14398                 moved = true;
14399             }
14400             // then make sure top/left isn't negative
14401             if(x < s.left){
14402                 x = s.left;
14403                 moved = true;
14404             }
14405             if(y < s.top){
14406                 y = s.top;
14407                 moved = true;
14408             }
14409             if(moved){
14410                 // cache xy
14411                 this.xy = [x, y];
14412                 if(this.isVisible()){
14413                     this.el.setLocation(x, y);
14414                     this.adjustAssets();
14415                 }
14416             }
14417         }
14418     },
14419
14420     // private
14421     onDrag : function(){
14422         if(!this.proxyDrag){
14423             this.xy = this.el.getXY();
14424             this.adjustAssets();
14425         }
14426     },
14427
14428     // private
14429     adjustAssets : function(doShow){
14430         var x = this.xy[0], y = this.xy[1];
14431         var w = this.size.width, h = this.size.height;
14432         if(doShow === true){
14433             if(this.shadow){
14434                 this.shadow.show(this.el);
14435             }
14436             if(this.shim){
14437                 this.shim.show();
14438             }
14439         }
14440         if(this.shadow && this.shadow.isVisible()){
14441             this.shadow.show(this.el);
14442         }
14443         if(this.shim && this.shim.isVisible()){
14444             this.shim.setBounds(x, y, w, h);
14445         }
14446     },
14447
14448     // private
14449     adjustViewport : function(w, h){
14450         if(!w || !h){
14451             w = Roo.lib.Dom.getViewWidth();
14452             h = Roo.lib.Dom.getViewHeight();
14453         }
14454         // cache the size
14455         this.viewSize = [w, h];
14456         if(this.modal && this.mask.isVisible()){
14457             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14458             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14459         }
14460         if(this.isVisible()){
14461             this.constrainXY();
14462         }
14463     },
14464
14465     /**
14466      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14467      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14468      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14469      */
14470     destroy : function(removeEl){
14471         if(this.isVisible()){
14472             this.animateTarget = null;
14473             this.hide();
14474         }
14475         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14476         if(this.tabs){
14477             this.tabs.destroy(removeEl);
14478         }
14479         Roo.destroy(
14480              this.shim,
14481              this.proxy,
14482              this.resizer,
14483              this.close,
14484              this.mask
14485         );
14486         if(this.dd){
14487             this.dd.unreg();
14488         }
14489         if(this.buttons){
14490            for(var i = 0, len = this.buttons.length; i < len; i++){
14491                this.buttons[i].destroy();
14492            }
14493         }
14494         this.el.removeAllListeners();
14495         if(removeEl === true){
14496             this.el.update("");
14497             this.el.remove();
14498         }
14499         Roo.DialogManager.unregister(this);
14500     },
14501
14502     // private
14503     startMove : function(){
14504         if(this.proxyDrag){
14505             this.proxy.show();
14506         }
14507         if(this.constraintoviewport !== false){
14508             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14509         }
14510     },
14511
14512     // private
14513     endMove : function(){
14514         if(!this.proxyDrag){
14515             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14516         }else{
14517             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14518             this.proxy.hide();
14519         }
14520         this.refreshSize();
14521         this.adjustAssets();
14522         this.focus();
14523         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14524     },
14525
14526     /**
14527      * Brings this dialog to the front of any other visible dialogs
14528      * @return {Roo.BasicDialog} this
14529      */
14530     toFront : function(){
14531         Roo.DialogManager.bringToFront(this);
14532         return this;
14533     },
14534
14535     /**
14536      * Sends this dialog to the back (under) of any other visible dialogs
14537      * @return {Roo.BasicDialog} this
14538      */
14539     toBack : function(){
14540         Roo.DialogManager.sendToBack(this);
14541         return this;
14542     },
14543
14544     /**
14545      * Centers this dialog in the viewport
14546      * @return {Roo.BasicDialog} this
14547      */
14548     center : function(){
14549         var xy = this.el.getCenterXY(true);
14550         this.moveTo(xy[0], xy[1]);
14551         return this;
14552     },
14553
14554     /**
14555      * Moves the dialog's top-left corner to the specified point
14556      * @param {Number} x
14557      * @param {Number} y
14558      * @return {Roo.BasicDialog} this
14559      */
14560     moveTo : function(x, y){
14561         this.xy = [x,y];
14562         if(this.isVisible()){
14563             this.el.setXY(this.xy);
14564             this.adjustAssets();
14565         }
14566         return this;
14567     },
14568
14569     /**
14570      * Aligns the dialog to the specified element
14571      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14572      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14573      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14574      * @return {Roo.BasicDialog} this
14575      */
14576     alignTo : function(element, position, offsets){
14577         this.xy = this.el.getAlignToXY(element, position, offsets);
14578         if(this.isVisible()){
14579             this.el.setXY(this.xy);
14580             this.adjustAssets();
14581         }
14582         return this;
14583     },
14584
14585     /**
14586      * Anchors an element to another element and realigns it when the window is resized.
14587      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14588      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14589      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14590      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14591      * is a number, it is used as the buffer delay (defaults to 50ms).
14592      * @return {Roo.BasicDialog} this
14593      */
14594     anchorTo : function(el, alignment, offsets, monitorScroll){
14595         var action = function(){
14596             this.alignTo(el, alignment, offsets);
14597         };
14598         Roo.EventManager.onWindowResize(action, this);
14599         var tm = typeof monitorScroll;
14600         if(tm != 'undefined'){
14601             Roo.EventManager.on(window, 'scroll', action, this,
14602                 {buffer: tm == 'number' ? monitorScroll : 50});
14603         }
14604         action.call(this);
14605         return this;
14606     },
14607
14608     /**
14609      * Returns true if the dialog is visible
14610      * @return {Boolean}
14611      */
14612     isVisible : function(){
14613         return this.el.isVisible();
14614     },
14615
14616     // private
14617     animHide : function(callback){
14618         var b = Roo.get(this.animateTarget).getBox();
14619         this.proxy.show();
14620         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14621         this.el.hide();
14622         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14623                     this.hideEl.createDelegate(this, [callback]));
14624     },
14625
14626     /**
14627      * Hides the dialog.
14628      * @param {Function} callback (optional) Function to call when the dialog is hidden
14629      * @return {Roo.BasicDialog} this
14630      */
14631     hide : function(callback){
14632         if (this.fireEvent("beforehide", this) === false){
14633             return;
14634         }
14635         if(this.shadow){
14636             this.shadow.hide();
14637         }
14638         if(this.shim) {
14639           this.shim.hide();
14640         }
14641         // sometimes animateTarget seems to get set.. causing problems...
14642         // this just double checks..
14643         if(this.animateTarget && Roo.get(this.animateTarget)) {
14644            this.animHide(callback);
14645         }else{
14646             this.el.hide();
14647             this.hideEl(callback);
14648         }
14649         return this;
14650     },
14651
14652     // private
14653     hideEl : function(callback){
14654         this.proxy.hide();
14655         if(this.modal){
14656             this.mask.hide();
14657             Roo.get(document.body).removeClass("x-body-masked");
14658         }
14659         this.fireEvent("hide", this);
14660         if(typeof callback == "function"){
14661             callback();
14662         }
14663     },
14664
14665     // private
14666     hideAction : function(){
14667         this.setLeft("-10000px");
14668         this.setTop("-10000px");
14669         this.setStyle("visibility", "hidden");
14670     },
14671
14672     // private
14673     refreshSize : function(){
14674         this.size = this.el.getSize();
14675         this.xy = this.el.getXY();
14676         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14677     },
14678
14679     // private
14680     // z-index is managed by the DialogManager and may be overwritten at any time
14681     setZIndex : function(index){
14682         if(this.modal){
14683             this.mask.setStyle("z-index", index);
14684         }
14685         if(this.shim){
14686             this.shim.setStyle("z-index", ++index);
14687         }
14688         if(this.shadow){
14689             this.shadow.setZIndex(++index);
14690         }
14691         this.el.setStyle("z-index", ++index);
14692         if(this.proxy){
14693             this.proxy.setStyle("z-index", ++index);
14694         }
14695         if(this.resizer){
14696             this.resizer.proxy.setStyle("z-index", ++index);
14697         }
14698
14699         this.lastZIndex = index;
14700     },
14701
14702     /**
14703      * Returns the element for this dialog
14704      * @return {Roo.Element} The underlying dialog Element
14705      */
14706     getEl : function(){
14707         return this.el;
14708     }
14709 });
14710
14711 /**
14712  * @class Roo.DialogManager
14713  * Provides global access to BasicDialogs that have been created and
14714  * support for z-indexing (layering) multiple open dialogs.
14715  */
14716 Roo.DialogManager = function(){
14717     var list = {};
14718     var accessList = [];
14719     var front = null;
14720
14721     // private
14722     var sortDialogs = function(d1, d2){
14723         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14724     };
14725
14726     // private
14727     var orderDialogs = function(){
14728         accessList.sort(sortDialogs);
14729         var seed = Roo.DialogManager.zseed;
14730         for(var i = 0, len = accessList.length; i < len; i++){
14731             var dlg = accessList[i];
14732             if(dlg){
14733                 dlg.setZIndex(seed + (i*10));
14734             }
14735         }
14736     };
14737
14738     return {
14739         /**
14740          * The starting z-index for BasicDialogs (defaults to 9000)
14741          * @type Number The z-index value
14742          */
14743         zseed : 9000,
14744
14745         // private
14746         register : function(dlg){
14747             list[dlg.id] = dlg;
14748             accessList.push(dlg);
14749         },
14750
14751         // private
14752         unregister : function(dlg){
14753             delete list[dlg.id];
14754             var i=0;
14755             var len=0;
14756             if(!accessList.indexOf){
14757                 for(  i = 0, len = accessList.length; i < len; i++){
14758                     if(accessList[i] == dlg){
14759                         accessList.splice(i, 1);
14760                         return;
14761                     }
14762                 }
14763             }else{
14764                  i = accessList.indexOf(dlg);
14765                 if(i != -1){
14766                     accessList.splice(i, 1);
14767                 }
14768             }
14769         },
14770
14771         /**
14772          * Gets a registered dialog by id
14773          * @param {String/Object} id The id of the dialog or a dialog
14774          * @return {Roo.BasicDialog} this
14775          */
14776         get : function(id){
14777             return typeof id == "object" ? id : list[id];
14778         },
14779
14780         /**
14781          * Brings the specified dialog to the front
14782          * @param {String/Object} dlg The id of the dialog or a dialog
14783          * @return {Roo.BasicDialog} this
14784          */
14785         bringToFront : function(dlg){
14786             dlg = this.get(dlg);
14787             if(dlg != front){
14788                 front = dlg;
14789                 dlg._lastAccess = new Date().getTime();
14790                 orderDialogs();
14791             }
14792             return dlg;
14793         },
14794
14795         /**
14796          * Sends the specified dialog to the back
14797          * @param {String/Object} dlg The id of the dialog or a dialog
14798          * @return {Roo.BasicDialog} this
14799          */
14800         sendToBack : function(dlg){
14801             dlg = this.get(dlg);
14802             dlg._lastAccess = -(new Date().getTime());
14803             orderDialogs();
14804             return dlg;
14805         },
14806
14807         /**
14808          * Hides all dialogs
14809          */
14810         hideAll : function(){
14811             for(var id in list){
14812                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14813                     list[id].hide();
14814                 }
14815             }
14816         }
14817     };
14818 }();
14819
14820 /**
14821  * @class Roo.LayoutDialog
14822  * @extends Roo.BasicDialog
14823  * Dialog which provides adjustments for working with a layout in a Dialog.
14824  * Add your necessary layout config options to the dialog's config.<br>
14825  * Example usage (including a nested layout):
14826  * <pre><code>
14827 if(!dialog){
14828     dialog = new Roo.LayoutDialog("download-dlg", {
14829         modal: true,
14830         width:600,
14831         height:450,
14832         shadow:true,
14833         minWidth:500,
14834         minHeight:350,
14835         autoTabs:true,
14836         proxyDrag:true,
14837         // layout config merges with the dialog config
14838         center:{
14839             tabPosition: "top",
14840             alwaysShowTabs: true
14841         }
14842     });
14843     dialog.addKeyListener(27, dialog.hide, dialog);
14844     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14845     dialog.addButton("Build It!", this.getDownload, this);
14846
14847     // we can even add nested layouts
14848     var innerLayout = new Roo.BorderLayout("dl-inner", {
14849         east: {
14850             initialSize: 200,
14851             autoScroll:true,
14852             split:true
14853         },
14854         center: {
14855             autoScroll:true
14856         }
14857     });
14858     innerLayout.beginUpdate();
14859     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14860     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14861     innerLayout.endUpdate(true);
14862
14863     var layout = dialog.getLayout();
14864     layout.beginUpdate();
14865     layout.add("center", new Roo.ContentPanel("standard-panel",
14866                         {title: "Download the Source", fitToFrame:true}));
14867     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14868                {title: "Build your own roo.js"}));
14869     layout.getRegion("center").showPanel(sp);
14870     layout.endUpdate();
14871 }
14872 </code></pre>
14873     * @constructor
14874     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14875     * @param {Object} config configuration options
14876   */
14877 Roo.LayoutDialog = function(el, cfg){
14878     
14879     var config=  cfg;
14880     if (typeof(cfg) == 'undefined') {
14881         config = Roo.apply({}, el);
14882         // not sure why we use documentElement here.. - it should always be body.
14883         // IE7 borks horribly if we use documentElement.
14884         // webkit also does not like documentElement - it creates a body element...
14885         el = Roo.get( document.body || document.documentElement ).createChild();
14886         //config.autoCreate = true;
14887     }
14888     
14889     
14890     config.autoTabs = false;
14891     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14892     this.body.setStyle({overflow:"hidden", position:"relative"});
14893     this.layout = new Roo.BorderLayout(this.body.dom, config);
14894     this.layout.monitorWindowResize = false;
14895     this.el.addClass("x-dlg-auto-layout");
14896     // fix case when center region overwrites center function
14897     this.center = Roo.BasicDialog.prototype.center;
14898     this.on("show", this.layout.layout, this.layout, true);
14899     if (config.items) {
14900         var xitems = config.items;
14901         delete config.items;
14902         Roo.each(xitems, this.addxtype, this);
14903     }
14904     
14905     
14906 };
14907 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14908     /**
14909      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14910      * @deprecated
14911      */
14912     endUpdate : function(){
14913         this.layout.endUpdate();
14914     },
14915
14916     /**
14917      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14918      *  @deprecated
14919      */
14920     beginUpdate : function(){
14921         this.layout.beginUpdate();
14922     },
14923
14924     /**
14925      * Get the BorderLayout for this dialog
14926      * @return {Roo.BorderLayout}
14927      */
14928     getLayout : function(){
14929         return this.layout;
14930     },
14931
14932     showEl : function(){
14933         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14934         if(Roo.isIE7){
14935             this.layout.layout();
14936         }
14937     },
14938
14939     // private
14940     // Use the syncHeightBeforeShow config option to control this automatically
14941     syncBodyHeight : function(){
14942         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14943         if(this.layout){this.layout.layout();}
14944     },
14945     
14946       /**
14947      * Add an xtype element (actually adds to the layout.)
14948      * @return {Object} xdata xtype object data.
14949      */
14950     
14951     addxtype : function(c) {
14952         return this.layout.addxtype(c);
14953     }
14954 });/*
14955  * Based on:
14956  * Ext JS Library 1.1.1
14957  * Copyright(c) 2006-2007, Ext JS, LLC.
14958  *
14959  * Originally Released Under LGPL - original licence link has changed is not relivant.
14960  *
14961  * Fork - LGPL
14962  * <script type="text/javascript">
14963  */
14964  
14965 /**
14966  * @class Roo.MessageBox
14967  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
14968  * Example usage:
14969  *<pre><code>
14970 // Basic alert:
14971 Roo.Msg.alert('Status', 'Changes saved successfully.');
14972
14973 // Prompt for user data:
14974 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
14975     if (btn == 'ok'){
14976         // process text value...
14977     }
14978 });
14979
14980 // Show a dialog using config options:
14981 Roo.Msg.show({
14982    title:'Save Changes?',
14983    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
14984    buttons: Roo.Msg.YESNOCANCEL,
14985    fn: processResult,
14986    animEl: 'elId'
14987 });
14988 </code></pre>
14989  * @singleton
14990  */
14991 Roo.MessageBox = function(){
14992     var dlg, opt, mask, waitTimer;
14993     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
14994     var buttons, activeTextEl, bwidth;
14995
14996     // private
14997     var handleButton = function(button){
14998         dlg.hide();
14999         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15000     };
15001
15002     // private
15003     var handleHide = function(){
15004         if(opt && opt.cls){
15005             dlg.el.removeClass(opt.cls);
15006         }
15007         if(waitTimer){
15008             Roo.TaskMgr.stop(waitTimer);
15009             waitTimer = null;
15010         }
15011     };
15012
15013     // private
15014     var updateButtons = function(b){
15015         var width = 0;
15016         if(!b){
15017             buttons["ok"].hide();
15018             buttons["cancel"].hide();
15019             buttons["yes"].hide();
15020             buttons["no"].hide();
15021             dlg.footer.dom.style.display = 'none';
15022             return width;
15023         }
15024         dlg.footer.dom.style.display = '';
15025         for(var k in buttons){
15026             if(typeof buttons[k] != "function"){
15027                 if(b[k]){
15028                     buttons[k].show();
15029                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15030                     width += buttons[k].el.getWidth()+15;
15031                 }else{
15032                     buttons[k].hide();
15033                 }
15034             }
15035         }
15036         return width;
15037     };
15038
15039     // private
15040     var handleEsc = function(d, k, e){
15041         if(opt && opt.closable !== false){
15042             dlg.hide();
15043         }
15044         if(e){
15045             e.stopEvent();
15046         }
15047     };
15048
15049     return {
15050         /**
15051          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15052          * @return {Roo.BasicDialog} The BasicDialog element
15053          */
15054         getDialog : function(){
15055            if(!dlg){
15056                 dlg = new Roo.BasicDialog("x-msg-box", {
15057                     autoCreate : true,
15058                     shadow: true,
15059                     draggable: true,
15060                     resizable:false,
15061                     constraintoviewport:false,
15062                     fixedcenter:true,
15063                     collapsible : false,
15064                     shim:true,
15065                     modal: true,
15066                     width:400, height:100,
15067                     buttonAlign:"center",
15068                     closeClick : function(){
15069                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15070                             handleButton("no");
15071                         }else{
15072                             handleButton("cancel");
15073                         }
15074                     }
15075                 });
15076                 dlg.on("hide", handleHide);
15077                 mask = dlg.mask;
15078                 dlg.addKeyListener(27, handleEsc);
15079                 buttons = {};
15080                 var bt = this.buttonText;
15081                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15082                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15083                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15084                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15085                 bodyEl = dlg.body.createChild({
15086
15087                     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>'
15088                 });
15089                 msgEl = bodyEl.dom.firstChild;
15090                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15091                 textboxEl.enableDisplayMode();
15092                 textboxEl.addKeyListener([10,13], function(){
15093                     if(dlg.isVisible() && opt && opt.buttons){
15094                         if(opt.buttons.ok){
15095                             handleButton("ok");
15096                         }else if(opt.buttons.yes){
15097                             handleButton("yes");
15098                         }
15099                     }
15100                 });
15101                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15102                 textareaEl.enableDisplayMode();
15103                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15104                 progressEl.enableDisplayMode();
15105                 var pf = progressEl.dom.firstChild;
15106                 if (pf) {
15107                     pp = Roo.get(pf.firstChild);
15108                     pp.setHeight(pf.offsetHeight);
15109                 }
15110                 
15111             }
15112             return dlg;
15113         },
15114
15115         /**
15116          * Updates the message box body text
15117          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15118          * the XHTML-compliant non-breaking space character '&amp;#160;')
15119          * @return {Roo.MessageBox} This message box
15120          */
15121         updateText : function(text){
15122             if(!dlg.isVisible() && !opt.width){
15123                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15124             }
15125             msgEl.innerHTML = text || '&#160;';
15126       
15127             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15128             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15129             var w = Math.max(
15130                     Math.min(opt.width || cw , this.maxWidth), 
15131                     Math.max(opt.minWidth || this.minWidth, bwidth)
15132             );
15133             if(opt.prompt){
15134                 activeTextEl.setWidth(w);
15135             }
15136             if(dlg.isVisible()){
15137                 dlg.fixedcenter = false;
15138             }
15139             // to big, make it scroll. = But as usual stupid IE does not support
15140             // !important..
15141             
15142             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15143                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15144                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15145             } else {
15146                 bodyEl.dom.style.height = '';
15147                 bodyEl.dom.style.overflowY = '';
15148             }
15149             if (cw > w) {
15150                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15151             } else {
15152                 bodyEl.dom.style.overflowX = '';
15153             }
15154             
15155             dlg.setContentSize(w, bodyEl.getHeight());
15156             if(dlg.isVisible()){
15157                 dlg.fixedcenter = true;
15158             }
15159             return this;
15160         },
15161
15162         /**
15163          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15164          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15165          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15166          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15167          * @return {Roo.MessageBox} This message box
15168          */
15169         updateProgress : function(value, text){
15170             if(text){
15171                 this.updateText(text);
15172             }
15173             if (pp) { // weird bug on my firefox - for some reason this is not defined
15174                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15175             }
15176             return this;
15177         },        
15178
15179         /**
15180          * Returns true if the message box is currently displayed
15181          * @return {Boolean} True if the message box is visible, else false
15182          */
15183         isVisible : function(){
15184             return dlg && dlg.isVisible();  
15185         },
15186
15187         /**
15188          * Hides the message box if it is displayed
15189          */
15190         hide : function(){
15191             if(this.isVisible()){
15192                 dlg.hide();
15193             }  
15194         },
15195
15196         /**
15197          * Displays a new message box, or reinitializes an existing message box, based on the config options
15198          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15199          * The following config object properties are supported:
15200          * <pre>
15201 Property    Type             Description
15202 ----------  ---------------  ------------------------------------------------------------------------------------
15203 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15204                                    closes (defaults to undefined)
15205 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15206                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15207 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15208                                    progress and wait dialogs will ignore this property and always hide the
15209                                    close button as they can only be closed programmatically.
15210 cls               String           A custom CSS class to apply to the message box element
15211 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15212                                    displayed (defaults to 75)
15213 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15214                                    function will be btn (the name of the button that was clicked, if applicable,
15215                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15216                                    Progress and wait dialogs will ignore this option since they do not respond to
15217                                    user actions and can only be closed programmatically, so any required function
15218                                    should be called by the same code after it closes the dialog.
15219 icon              String           A CSS class that provides a background image to be used as an icon for
15220                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15221 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15222 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15223 modal             Boolean          False to allow user interaction with the page while the message box is
15224                                    displayed (defaults to true)
15225 msg               String           A string that will replace the existing message box body text (defaults
15226                                    to the XHTML-compliant non-breaking space character '&#160;')
15227 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15228 progress          Boolean          True to display a progress bar (defaults to false)
15229 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15230 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15231 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15232 title             String           The title text
15233 value             String           The string value to set into the active textbox element if displayed
15234 wait              Boolean          True to display a progress bar (defaults to false)
15235 width             Number           The width of the dialog in pixels
15236 </pre>
15237          *
15238          * Example usage:
15239          * <pre><code>
15240 Roo.Msg.show({
15241    title: 'Address',
15242    msg: 'Please enter your address:',
15243    width: 300,
15244    buttons: Roo.MessageBox.OKCANCEL,
15245    multiline: true,
15246    fn: saveAddress,
15247    animEl: 'addAddressBtn'
15248 });
15249 </code></pre>
15250          * @param {Object} config Configuration options
15251          * @return {Roo.MessageBox} This message box
15252          */
15253         show : function(options)
15254         {
15255             
15256             // this causes nightmares if you show one dialog after another
15257             // especially on callbacks..
15258              
15259             if(this.isVisible()){
15260                 
15261                 this.hide();
15262                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15263                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15264                 Roo.log("New Dialog Message:" +  options.msg )
15265                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15266                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15267                 
15268             }
15269             var d = this.getDialog();
15270             opt = options;
15271             d.setTitle(opt.title || "&#160;");
15272             d.close.setDisplayed(opt.closable !== false);
15273             activeTextEl = textboxEl;
15274             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15275             if(opt.prompt){
15276                 if(opt.multiline){
15277                     textboxEl.hide();
15278                     textareaEl.show();
15279                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15280                         opt.multiline : this.defaultTextHeight);
15281                     activeTextEl = textareaEl;
15282                 }else{
15283                     textboxEl.show();
15284                     textareaEl.hide();
15285                 }
15286             }else{
15287                 textboxEl.hide();
15288                 textareaEl.hide();
15289             }
15290             progressEl.setDisplayed(opt.progress === true);
15291             this.updateProgress(0);
15292             activeTextEl.dom.value = opt.value || "";
15293             if(opt.prompt){
15294                 dlg.setDefaultButton(activeTextEl);
15295             }else{
15296                 var bs = opt.buttons;
15297                 var db = null;
15298                 if(bs && bs.ok){
15299                     db = buttons["ok"];
15300                 }else if(bs && bs.yes){
15301                     db = buttons["yes"];
15302                 }
15303                 dlg.setDefaultButton(db);
15304             }
15305             bwidth = updateButtons(opt.buttons);
15306             this.updateText(opt.msg);
15307             if(opt.cls){
15308                 d.el.addClass(opt.cls);
15309             }
15310             d.proxyDrag = opt.proxyDrag === true;
15311             d.modal = opt.modal !== false;
15312             d.mask = opt.modal !== false ? mask : false;
15313             if(!d.isVisible()){
15314                 // force it to the end of the z-index stack so it gets a cursor in FF
15315                 document.body.appendChild(dlg.el.dom);
15316                 d.animateTarget = null;
15317                 d.show(options.animEl);
15318             }
15319             return this;
15320         },
15321
15322         /**
15323          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15324          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15325          * and closing the message box when the process is complete.
15326          * @param {String} title The title bar text
15327          * @param {String} msg The message box body text
15328          * @return {Roo.MessageBox} This message box
15329          */
15330         progress : function(title, msg){
15331             this.show({
15332                 title : title,
15333                 msg : msg,
15334                 buttons: false,
15335                 progress:true,
15336                 closable:false,
15337                 minWidth: this.minProgressWidth,
15338                 modal : true
15339             });
15340             return this;
15341         },
15342
15343         /**
15344          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15345          * If a callback function is passed it will be called after the user clicks the button, and the
15346          * id of the button that was clicked will be passed as the only parameter to the callback
15347          * (could also be the top-right close button).
15348          * @param {String} title The title bar text
15349          * @param {String} msg The message box body text
15350          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15351          * @param {Object} scope (optional) The scope of the callback function
15352          * @return {Roo.MessageBox} This message box
15353          */
15354         alert : function(title, msg, fn, scope){
15355             this.show({
15356                 title : title,
15357                 msg : msg,
15358                 buttons: this.OK,
15359                 fn: fn,
15360                 scope : scope,
15361                 modal : true
15362             });
15363             return this;
15364         },
15365
15366         /**
15367          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15368          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15369          * You are responsible for closing the message box when the process is complete.
15370          * @param {String} msg The message box body text
15371          * @param {String} title (optional) The title bar text
15372          * @return {Roo.MessageBox} This message box
15373          */
15374         wait : function(msg, title){
15375             this.show({
15376                 title : title,
15377                 msg : msg,
15378                 buttons: false,
15379                 closable:false,
15380                 progress:true,
15381                 modal:true,
15382                 width:300,
15383                 wait:true
15384             });
15385             waitTimer = Roo.TaskMgr.start({
15386                 run: function(i){
15387                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15388                 },
15389                 interval: 1000
15390             });
15391             return this;
15392         },
15393
15394         /**
15395          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15396          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15397          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15398          * @param {String} title The title bar text
15399          * @param {String} msg The message box body text
15400          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15401          * @param {Object} scope (optional) The scope of the callback function
15402          * @return {Roo.MessageBox} This message box
15403          */
15404         confirm : function(title, msg, fn, scope){
15405             this.show({
15406                 title : title,
15407                 msg : msg,
15408                 buttons: this.YESNO,
15409                 fn: fn,
15410                 scope : scope,
15411                 modal : true
15412             });
15413             return this;
15414         },
15415
15416         /**
15417          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15418          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15419          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15420          * (could also be the top-right close button) and the text that was entered will be passed as the two
15421          * parameters to the callback.
15422          * @param {String} title The title bar text
15423          * @param {String} msg The message box body text
15424          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15425          * @param {Object} scope (optional) The scope of the callback function
15426          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15427          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15428          * @return {Roo.MessageBox} This message box
15429          */
15430         prompt : function(title, msg, fn, scope, multiline){
15431             this.show({
15432                 title : title,
15433                 msg : msg,
15434                 buttons: this.OKCANCEL,
15435                 fn: fn,
15436                 minWidth:250,
15437                 scope : scope,
15438                 prompt:true,
15439                 multiline: multiline,
15440                 modal : true
15441             });
15442             return this;
15443         },
15444
15445         /**
15446          * Button config that displays a single OK button
15447          * @type Object
15448          */
15449         OK : {ok:true},
15450         /**
15451          * Button config that displays Yes and No buttons
15452          * @type Object
15453          */
15454         YESNO : {yes:true, no:true},
15455         /**
15456          * Button config that displays OK and Cancel buttons
15457          * @type Object
15458          */
15459         OKCANCEL : {ok:true, cancel:true},
15460         /**
15461          * Button config that displays Yes, No and Cancel buttons
15462          * @type Object
15463          */
15464         YESNOCANCEL : {yes:true, no:true, cancel:true},
15465
15466         /**
15467          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15468          * @type Number
15469          */
15470         defaultTextHeight : 75,
15471         /**
15472          * The maximum width in pixels of the message box (defaults to 600)
15473          * @type Number
15474          */
15475         maxWidth : 600,
15476         /**
15477          * The minimum width in pixels of the message box (defaults to 100)
15478          * @type Number
15479          */
15480         minWidth : 100,
15481         /**
15482          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15483          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15484          * @type Number
15485          */
15486         minProgressWidth : 250,
15487         /**
15488          * An object containing the default button text strings that can be overriden for localized language support.
15489          * Supported properties are: ok, cancel, yes and no.
15490          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15491          * @type Object
15492          */
15493         buttonText : {
15494             ok : "OK",
15495             cancel : "Cancel",
15496             yes : "Yes",
15497             no : "No"
15498         }
15499     };
15500 }();
15501
15502 /**
15503  * Shorthand for {@link Roo.MessageBox}
15504  */
15505 Roo.Msg = Roo.MessageBox;/*
15506  * Based on:
15507  * Ext JS Library 1.1.1
15508  * Copyright(c) 2006-2007, Ext JS, LLC.
15509  *
15510  * Originally Released Under LGPL - original licence link has changed is not relivant.
15511  *
15512  * Fork - LGPL
15513  * <script type="text/javascript">
15514  */
15515 /**
15516  * @class Roo.QuickTips
15517  * Provides attractive and customizable tooltips for any element.
15518  * @singleton
15519  */
15520 Roo.QuickTips = function(){
15521     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15522     var ce, bd, xy, dd;
15523     var visible = false, disabled = true, inited = false;
15524     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15525     
15526     var onOver = function(e){
15527         if(disabled){
15528             return;
15529         }
15530         var t = e.getTarget();
15531         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15532             return;
15533         }
15534         if(ce && t == ce.el){
15535             clearTimeout(hideProc);
15536             return;
15537         }
15538         if(t && tagEls[t.id]){
15539             tagEls[t.id].el = t;
15540             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15541             return;
15542         }
15543         var ttp, et = Roo.fly(t);
15544         var ns = cfg.namespace;
15545         if(tm.interceptTitles && t.title){
15546             ttp = t.title;
15547             t.qtip = ttp;
15548             t.removeAttribute("title");
15549             e.preventDefault();
15550         }else{
15551             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15552         }
15553         if(ttp){
15554             showProc = show.defer(tm.showDelay, tm, [{
15555                 el: t, 
15556                 text: ttp, 
15557                 width: et.getAttributeNS(ns, cfg.width),
15558                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15559                 title: et.getAttributeNS(ns, cfg.title),
15560                     cls: et.getAttributeNS(ns, cfg.cls)
15561             }]);
15562         }
15563     };
15564     
15565     var onOut = function(e){
15566         clearTimeout(showProc);
15567         var t = e.getTarget();
15568         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15569             hideProc = setTimeout(hide, tm.hideDelay);
15570         }
15571     };
15572     
15573     var onMove = function(e){
15574         if(disabled){
15575             return;
15576         }
15577         xy = e.getXY();
15578         xy[1] += 18;
15579         if(tm.trackMouse && ce){
15580             el.setXY(xy);
15581         }
15582     };
15583     
15584     var onDown = function(e){
15585         clearTimeout(showProc);
15586         clearTimeout(hideProc);
15587         if(!e.within(el)){
15588             if(tm.hideOnClick){
15589                 hide();
15590                 tm.disable();
15591                 tm.enable.defer(100, tm);
15592             }
15593         }
15594     };
15595     
15596     var getPad = function(){
15597         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15598     };
15599
15600     var show = function(o){
15601         if(disabled){
15602             return;
15603         }
15604         clearTimeout(dismissProc);
15605         ce = o;
15606         if(removeCls){ // in case manually hidden
15607             el.removeClass(removeCls);
15608             removeCls = null;
15609         }
15610         if(ce.cls){
15611             el.addClass(ce.cls);
15612             removeCls = ce.cls;
15613         }
15614         if(ce.title){
15615             tipTitle.update(ce.title);
15616             tipTitle.show();
15617         }else{
15618             tipTitle.update('');
15619             tipTitle.hide();
15620         }
15621         el.dom.style.width  = tm.maxWidth+'px';
15622         //tipBody.dom.style.width = '';
15623         tipBodyText.update(o.text);
15624         var p = getPad(), w = ce.width;
15625         if(!w){
15626             var td = tipBodyText.dom;
15627             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15628             if(aw > tm.maxWidth){
15629                 w = tm.maxWidth;
15630             }else if(aw < tm.minWidth){
15631                 w = tm.minWidth;
15632             }else{
15633                 w = aw;
15634             }
15635         }
15636         //tipBody.setWidth(w);
15637         el.setWidth(parseInt(w, 10) + p);
15638         if(ce.autoHide === false){
15639             close.setDisplayed(true);
15640             if(dd){
15641                 dd.unlock();
15642             }
15643         }else{
15644             close.setDisplayed(false);
15645             if(dd){
15646                 dd.lock();
15647             }
15648         }
15649         if(xy){
15650             el.avoidY = xy[1]-18;
15651             el.setXY(xy);
15652         }
15653         if(tm.animate){
15654             el.setOpacity(.1);
15655             el.setStyle("visibility", "visible");
15656             el.fadeIn({callback: afterShow});
15657         }else{
15658             afterShow();
15659         }
15660     };
15661     
15662     var afterShow = function(){
15663         if(ce){
15664             el.show();
15665             esc.enable();
15666             if(tm.autoDismiss && ce.autoHide !== false){
15667                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15668             }
15669         }
15670     };
15671     
15672     var hide = function(noanim){
15673         clearTimeout(dismissProc);
15674         clearTimeout(hideProc);
15675         ce = null;
15676         if(el.isVisible()){
15677             esc.disable();
15678             if(noanim !== true && tm.animate){
15679                 el.fadeOut({callback: afterHide});
15680             }else{
15681                 afterHide();
15682             } 
15683         }
15684     };
15685     
15686     var afterHide = function(){
15687         el.hide();
15688         if(removeCls){
15689             el.removeClass(removeCls);
15690             removeCls = null;
15691         }
15692     };
15693     
15694     return {
15695         /**
15696         * @cfg {Number} minWidth
15697         * The minimum width of the quick tip (defaults to 40)
15698         */
15699        minWidth : 40,
15700         /**
15701         * @cfg {Number} maxWidth
15702         * The maximum width of the quick tip (defaults to 300)
15703         */
15704        maxWidth : 300,
15705         /**
15706         * @cfg {Boolean} interceptTitles
15707         * True to automatically use the element's DOM title value if available (defaults to false)
15708         */
15709        interceptTitles : false,
15710         /**
15711         * @cfg {Boolean} trackMouse
15712         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15713         */
15714        trackMouse : false,
15715         /**
15716         * @cfg {Boolean} hideOnClick
15717         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15718         */
15719        hideOnClick : true,
15720         /**
15721         * @cfg {Number} showDelay
15722         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15723         */
15724        showDelay : 500,
15725         /**
15726         * @cfg {Number} hideDelay
15727         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15728         */
15729        hideDelay : 200,
15730         /**
15731         * @cfg {Boolean} autoHide
15732         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15733         * Used in conjunction with hideDelay.
15734         */
15735        autoHide : true,
15736         /**
15737         * @cfg {Boolean}
15738         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15739         * (defaults to true).  Used in conjunction with autoDismissDelay.
15740         */
15741        autoDismiss : true,
15742         /**
15743         * @cfg {Number}
15744         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15745         */
15746        autoDismissDelay : 5000,
15747        /**
15748         * @cfg {Boolean} animate
15749         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15750         */
15751        animate : false,
15752
15753        /**
15754         * @cfg {String} title
15755         * Title text to display (defaults to '').  This can be any valid HTML markup.
15756         */
15757         title: '',
15758        /**
15759         * @cfg {String} text
15760         * Body text to display (defaults to '').  This can be any valid HTML markup.
15761         */
15762         text : '',
15763        /**
15764         * @cfg {String} cls
15765         * A CSS class to apply to the base quick tip element (defaults to '').
15766         */
15767         cls : '',
15768        /**
15769         * @cfg {Number} width
15770         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15771         * minWidth or maxWidth.
15772         */
15773         width : null,
15774
15775     /**
15776      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15777      * or display QuickTips in a page.
15778      */
15779        init : function(){
15780           tm = Roo.QuickTips;
15781           cfg = tm.tagConfig;
15782           if(!inited){
15783               if(!Roo.isReady){ // allow calling of init() before onReady
15784                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15785                   return;
15786               }
15787               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15788               el.fxDefaults = {stopFx: true};
15789               // maximum custom styling
15790               //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>');
15791               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>');              
15792               tipTitle = el.child('h3');
15793               tipTitle.enableDisplayMode("block");
15794               tipBody = el.child('div.x-tip-bd');
15795               tipBodyText = el.child('div.x-tip-bd-inner');
15796               //bdLeft = el.child('div.x-tip-bd-left');
15797               //bdRight = el.child('div.x-tip-bd-right');
15798               close = el.child('div.x-tip-close');
15799               close.enableDisplayMode("block");
15800               close.on("click", hide);
15801               var d = Roo.get(document);
15802               d.on("mousedown", onDown);
15803               d.on("mouseover", onOver);
15804               d.on("mouseout", onOut);
15805               d.on("mousemove", onMove);
15806               esc = d.addKeyListener(27, hide);
15807               esc.disable();
15808               if(Roo.dd.DD){
15809                   dd = el.initDD("default", null, {
15810                       onDrag : function(){
15811                           el.sync();  
15812                       }
15813                   });
15814                   dd.setHandleElId(tipTitle.id);
15815                   dd.lock();
15816               }
15817               inited = true;
15818           }
15819           this.enable(); 
15820        },
15821
15822     /**
15823      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15824      * are supported:
15825      * <pre>
15826 Property    Type                   Description
15827 ----------  ---------------------  ------------------------------------------------------------------------
15828 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15829      * </ul>
15830      * @param {Object} config The config object
15831      */
15832        register : function(config){
15833            var cs = config instanceof Array ? config : arguments;
15834            for(var i = 0, len = cs.length; i < len; i++) {
15835                var c = cs[i];
15836                var target = c.target;
15837                if(target){
15838                    if(target instanceof Array){
15839                        for(var j = 0, jlen = target.length; j < jlen; j++){
15840                            tagEls[target[j]] = c;
15841                        }
15842                    }else{
15843                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15844                    }
15845                }
15846            }
15847        },
15848
15849     /**
15850      * Removes this quick tip from its element and destroys it.
15851      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15852      */
15853        unregister : function(el){
15854            delete tagEls[Roo.id(el)];
15855        },
15856
15857     /**
15858      * Enable this quick tip.
15859      */
15860        enable : function(){
15861            if(inited && disabled){
15862                locks.pop();
15863                if(locks.length < 1){
15864                    disabled = false;
15865                }
15866            }
15867        },
15868
15869     /**
15870      * Disable this quick tip.
15871      */
15872        disable : function(){
15873           disabled = true;
15874           clearTimeout(showProc);
15875           clearTimeout(hideProc);
15876           clearTimeout(dismissProc);
15877           if(ce){
15878               hide(true);
15879           }
15880           locks.push(1);
15881        },
15882
15883     /**
15884      * Returns true if the quick tip is enabled, else false.
15885      */
15886        isEnabled : function(){
15887             return !disabled;
15888        },
15889
15890         // private
15891        tagConfig : {
15892            namespace : "ext",
15893            attribute : "qtip",
15894            width : "width",
15895            target : "target",
15896            title : "qtitle",
15897            hide : "hide",
15898            cls : "qclass"
15899        }
15900    };
15901 }();
15902
15903 // backwards compat
15904 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15905  * Based on:
15906  * Ext JS Library 1.1.1
15907  * Copyright(c) 2006-2007, Ext JS, LLC.
15908  *
15909  * Originally Released Under LGPL - original licence link has changed is not relivant.
15910  *
15911  * Fork - LGPL
15912  * <script type="text/javascript">
15913  */
15914  
15915
15916 /**
15917  * @class Roo.tree.TreePanel
15918  * @extends Roo.data.Tree
15919
15920  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15921  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15922  * @cfg {Boolean} enableDD true to enable drag and drop
15923  * @cfg {Boolean} enableDrag true to enable just drag
15924  * @cfg {Boolean} enableDrop true to enable just drop
15925  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15926  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15927  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15928  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15929  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15930  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15931  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15932  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15933  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15934  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15935  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15936  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15937  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15938  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15939  * @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>
15940  * @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>
15941  * 
15942  * @constructor
15943  * @param {String/HTMLElement/Element} el The container element
15944  * @param {Object} config
15945  */
15946 Roo.tree.TreePanel = function(el, config){
15947     var root = false;
15948     var loader = false;
15949     if (config.root) {
15950         root = config.root;
15951         delete config.root;
15952     }
15953     if (config.loader) {
15954         loader = config.loader;
15955         delete config.loader;
15956     }
15957     
15958     Roo.apply(this, config);
15959     Roo.tree.TreePanel.superclass.constructor.call(this);
15960     this.el = Roo.get(el);
15961     this.el.addClass('x-tree');
15962     //console.log(root);
15963     if (root) {
15964         this.setRootNode( Roo.factory(root, Roo.tree));
15965     }
15966     if (loader) {
15967         this.loader = Roo.factory(loader, Roo.tree);
15968     }
15969    /**
15970     * Read-only. The id of the container element becomes this TreePanel's id.
15971     */
15972     this.id = this.el.id;
15973     this.addEvents({
15974         /**
15975         * @event beforeload
15976         * Fires before a node is loaded, return false to cancel
15977         * @param {Node} node The node being loaded
15978         */
15979         "beforeload" : true,
15980         /**
15981         * @event load
15982         * Fires when a node is loaded
15983         * @param {Node} node The node that was loaded
15984         */
15985         "load" : true,
15986         /**
15987         * @event textchange
15988         * Fires when the text for a node is changed
15989         * @param {Node} node The node
15990         * @param {String} text The new text
15991         * @param {String} oldText The old text
15992         */
15993         "textchange" : true,
15994         /**
15995         * @event beforeexpand
15996         * Fires before a node is expanded, return false to cancel.
15997         * @param {Node} node The node
15998         * @param {Boolean} deep
15999         * @param {Boolean} anim
16000         */
16001         "beforeexpand" : true,
16002         /**
16003         * @event beforecollapse
16004         * Fires before a node is collapsed, return false to cancel.
16005         * @param {Node} node The node
16006         * @param {Boolean} deep
16007         * @param {Boolean} anim
16008         */
16009         "beforecollapse" : true,
16010         /**
16011         * @event expand
16012         * Fires when a node is expanded
16013         * @param {Node} node The node
16014         */
16015         "expand" : true,
16016         /**
16017         * @event disabledchange
16018         * Fires when the disabled status of a node changes
16019         * @param {Node} node The node
16020         * @param {Boolean} disabled
16021         */
16022         "disabledchange" : true,
16023         /**
16024         * @event collapse
16025         * Fires when a node is collapsed
16026         * @param {Node} node The node
16027         */
16028         "collapse" : true,
16029         /**
16030         * @event beforeclick
16031         * Fires before click processing on a node. Return false to cancel the default action.
16032         * @param {Node} node The node
16033         * @param {Roo.EventObject} e The event object
16034         */
16035         "beforeclick":true,
16036         /**
16037         * @event checkchange
16038         * Fires when a node with a checkbox's checked property changes
16039         * @param {Node} this This node
16040         * @param {Boolean} checked
16041         */
16042         "checkchange":true,
16043         /**
16044         * @event click
16045         * Fires when a node is clicked
16046         * @param {Node} node The node
16047         * @param {Roo.EventObject} e The event object
16048         */
16049         "click":true,
16050         /**
16051         * @event dblclick
16052         * Fires when a node is double clicked
16053         * @param {Node} node The node
16054         * @param {Roo.EventObject} e The event object
16055         */
16056         "dblclick":true,
16057         /**
16058         * @event contextmenu
16059         * Fires when a node is right clicked
16060         * @param {Node} node The node
16061         * @param {Roo.EventObject} e The event object
16062         */
16063         "contextmenu":true,
16064         /**
16065         * @event beforechildrenrendered
16066         * Fires right before the child nodes for a node are rendered
16067         * @param {Node} node The node
16068         */
16069         "beforechildrenrendered":true,
16070         /**
16071         * @event startdrag
16072         * Fires when a node starts being dragged
16073         * @param {Roo.tree.TreePanel} this
16074         * @param {Roo.tree.TreeNode} node
16075         * @param {event} e The raw browser event
16076         */ 
16077        "startdrag" : true,
16078        /**
16079         * @event enddrag
16080         * Fires when a drag operation is complete
16081         * @param {Roo.tree.TreePanel} this
16082         * @param {Roo.tree.TreeNode} node
16083         * @param {event} e The raw browser event
16084         */
16085        "enddrag" : true,
16086        /**
16087         * @event dragdrop
16088         * Fires when a dragged node is dropped on a valid DD target
16089         * @param {Roo.tree.TreePanel} this
16090         * @param {Roo.tree.TreeNode} node
16091         * @param {DD} dd The dd it was dropped on
16092         * @param {event} e The raw browser event
16093         */
16094        "dragdrop" : true,
16095        /**
16096         * @event beforenodedrop
16097         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16098         * passed to handlers has the following properties:<br />
16099         * <ul style="padding:5px;padding-left:16px;">
16100         * <li>tree - The TreePanel</li>
16101         * <li>target - The node being targeted for the drop</li>
16102         * <li>data - The drag data from the drag source</li>
16103         * <li>point - The point of the drop - append, above or below</li>
16104         * <li>source - The drag source</li>
16105         * <li>rawEvent - Raw mouse event</li>
16106         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16107         * to be inserted by setting them on this object.</li>
16108         * <li>cancel - Set this to true to cancel the drop.</li>
16109         * </ul>
16110         * @param {Object} dropEvent
16111         */
16112        "beforenodedrop" : true,
16113        /**
16114         * @event nodedrop
16115         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16116         * passed to handlers has the following properties:<br />
16117         * <ul style="padding:5px;padding-left:16px;">
16118         * <li>tree - The TreePanel</li>
16119         * <li>target - The node being targeted for the drop</li>
16120         * <li>data - The drag data from the drag source</li>
16121         * <li>point - The point of the drop - append, above or below</li>
16122         * <li>source - The drag source</li>
16123         * <li>rawEvent - Raw mouse event</li>
16124         * <li>dropNode - Dropped node(s).</li>
16125         * </ul>
16126         * @param {Object} dropEvent
16127         */
16128        "nodedrop" : true,
16129         /**
16130         * @event nodedragover
16131         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16132         * passed to handlers has the following properties:<br />
16133         * <ul style="padding:5px;padding-left:16px;">
16134         * <li>tree - The TreePanel</li>
16135         * <li>target - The node being targeted for the drop</li>
16136         * <li>data - The drag data from the drag source</li>
16137         * <li>point - The point of the drop - append, above or below</li>
16138         * <li>source - The drag source</li>
16139         * <li>rawEvent - Raw mouse event</li>
16140         * <li>dropNode - Drop node(s) provided by the source.</li>
16141         * <li>cancel - Set this to true to signal drop not allowed.</li>
16142         * </ul>
16143         * @param {Object} dragOverEvent
16144         */
16145        "nodedragover" : true
16146         
16147     });
16148     if(this.singleExpand){
16149        this.on("beforeexpand", this.restrictExpand, this);
16150     }
16151     if (this.editor) {
16152         this.editor.tree = this;
16153         this.editor = Roo.factory(this.editor, Roo.tree);
16154     }
16155     
16156     if (this.selModel) {
16157         this.selModel = Roo.factory(this.selModel, Roo.tree);
16158     }
16159    
16160 };
16161 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16162     rootVisible : true,
16163     animate: Roo.enableFx,
16164     lines : true,
16165     enableDD : false,
16166     hlDrop : Roo.enableFx,
16167   
16168     renderer: false,
16169     
16170     rendererTip: false,
16171     // private
16172     restrictExpand : function(node){
16173         var p = node.parentNode;
16174         if(p){
16175             if(p.expandedChild && p.expandedChild.parentNode == p){
16176                 p.expandedChild.collapse();
16177             }
16178             p.expandedChild = node;
16179         }
16180     },
16181
16182     // private override
16183     setRootNode : function(node){
16184         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16185         if(!this.rootVisible){
16186             node.ui = new Roo.tree.RootTreeNodeUI(node);
16187         }
16188         return node;
16189     },
16190
16191     /**
16192      * Returns the container element for this TreePanel
16193      */
16194     getEl : function(){
16195         return this.el;
16196     },
16197
16198     /**
16199      * Returns the default TreeLoader for this TreePanel
16200      */
16201     getLoader : function(){
16202         return this.loader;
16203     },
16204
16205     /**
16206      * Expand all nodes
16207      */
16208     expandAll : function(){
16209         this.root.expand(true);
16210     },
16211
16212     /**
16213      * Collapse all nodes
16214      */
16215     collapseAll : function(){
16216         this.root.collapse(true);
16217     },
16218
16219     /**
16220      * Returns the selection model used by this TreePanel
16221      */
16222     getSelectionModel : function(){
16223         if(!this.selModel){
16224             this.selModel = new Roo.tree.DefaultSelectionModel();
16225         }
16226         return this.selModel;
16227     },
16228
16229     /**
16230      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16231      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16232      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16233      * @return {Array}
16234      */
16235     getChecked : function(a, startNode){
16236         startNode = startNode || this.root;
16237         var r = [];
16238         var f = function(){
16239             if(this.attributes.checked){
16240                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16241             }
16242         }
16243         startNode.cascade(f);
16244         return r;
16245     },
16246
16247     /**
16248      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16249      * @param {String} path
16250      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16251      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16252      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16253      */
16254     expandPath : function(path, attr, callback){
16255         attr = attr || "id";
16256         var keys = path.split(this.pathSeparator);
16257         var curNode = this.root;
16258         if(curNode.attributes[attr] != keys[1]){ // invalid root
16259             if(callback){
16260                 callback(false, null);
16261             }
16262             return;
16263         }
16264         var index = 1;
16265         var f = function(){
16266             if(++index == keys.length){
16267                 if(callback){
16268                     callback(true, curNode);
16269                 }
16270                 return;
16271             }
16272             var c = curNode.findChild(attr, keys[index]);
16273             if(!c){
16274                 if(callback){
16275                     callback(false, curNode);
16276                 }
16277                 return;
16278             }
16279             curNode = c;
16280             c.expand(false, false, f);
16281         };
16282         curNode.expand(false, false, f);
16283     },
16284
16285     /**
16286      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16287      * @param {String} path
16288      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16289      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16290      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16291      */
16292     selectPath : function(path, attr, callback){
16293         attr = attr || "id";
16294         var keys = path.split(this.pathSeparator);
16295         var v = keys.pop();
16296         if(keys.length > 0){
16297             var f = function(success, node){
16298                 if(success && node){
16299                     var n = node.findChild(attr, v);
16300                     if(n){
16301                         n.select();
16302                         if(callback){
16303                             callback(true, n);
16304                         }
16305                     }else if(callback){
16306                         callback(false, n);
16307                     }
16308                 }else{
16309                     if(callback){
16310                         callback(false, n);
16311                     }
16312                 }
16313             };
16314             this.expandPath(keys.join(this.pathSeparator), attr, f);
16315         }else{
16316             this.root.select();
16317             if(callback){
16318                 callback(true, this.root);
16319             }
16320         }
16321     },
16322
16323     getTreeEl : function(){
16324         return this.el;
16325     },
16326
16327     /**
16328      * Trigger rendering of this TreePanel
16329      */
16330     render : function(){
16331         if (this.innerCt) {
16332             return this; // stop it rendering more than once!!
16333         }
16334         
16335         this.innerCt = this.el.createChild({tag:"ul",
16336                cls:"x-tree-root-ct " +
16337                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16338
16339         if(this.containerScroll){
16340             Roo.dd.ScrollManager.register(this.el);
16341         }
16342         if((this.enableDD || this.enableDrop) && !this.dropZone){
16343            /**
16344             * The dropZone used by this tree if drop is enabled
16345             * @type Roo.tree.TreeDropZone
16346             */
16347              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16348                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16349            });
16350         }
16351         if((this.enableDD || this.enableDrag) && !this.dragZone){
16352            /**
16353             * The dragZone used by this tree if drag is enabled
16354             * @type Roo.tree.TreeDragZone
16355             */
16356             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16357                ddGroup: this.ddGroup || "TreeDD",
16358                scroll: this.ddScroll
16359            });
16360         }
16361         this.getSelectionModel().init(this);
16362         if (!this.root) {
16363             Roo.log("ROOT not set in tree");
16364             return this;
16365         }
16366         this.root.render();
16367         if(!this.rootVisible){
16368             this.root.renderChildren();
16369         }
16370         return this;
16371     }
16372 });/*
16373  * Based on:
16374  * Ext JS Library 1.1.1
16375  * Copyright(c) 2006-2007, Ext JS, LLC.
16376  *
16377  * Originally Released Under LGPL - original licence link has changed is not relivant.
16378  *
16379  * Fork - LGPL
16380  * <script type="text/javascript">
16381  */
16382  
16383
16384 /**
16385  * @class Roo.tree.DefaultSelectionModel
16386  * @extends Roo.util.Observable
16387  * The default single selection for a TreePanel.
16388  * @param {Object} cfg Configuration
16389  */
16390 Roo.tree.DefaultSelectionModel = function(cfg){
16391    this.selNode = null;
16392    
16393    
16394    
16395    this.addEvents({
16396        /**
16397         * @event selectionchange
16398         * Fires when the selected node changes
16399         * @param {DefaultSelectionModel} this
16400         * @param {TreeNode} node the new selection
16401         */
16402        "selectionchange" : true,
16403
16404        /**
16405         * @event beforeselect
16406         * Fires before the selected node changes, return false to cancel the change
16407         * @param {DefaultSelectionModel} this
16408         * @param {TreeNode} node the new selection
16409         * @param {TreeNode} node the old selection
16410         */
16411        "beforeselect" : true
16412    });
16413    
16414     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16415 };
16416
16417 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16418     init : function(tree){
16419         this.tree = tree;
16420         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16421         tree.on("click", this.onNodeClick, this);
16422     },
16423     
16424     onNodeClick : function(node, e){
16425         if (e.ctrlKey && this.selNode == node)  {
16426             this.unselect(node);
16427             return;
16428         }
16429         this.select(node);
16430     },
16431     
16432     /**
16433      * Select a node.
16434      * @param {TreeNode} node The node to select
16435      * @return {TreeNode} The selected node
16436      */
16437     select : function(node){
16438         var last = this.selNode;
16439         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16440             if(last){
16441                 last.ui.onSelectedChange(false);
16442             }
16443             this.selNode = node;
16444             node.ui.onSelectedChange(true);
16445             this.fireEvent("selectionchange", this, node, last);
16446         }
16447         return node;
16448     },
16449     
16450     /**
16451      * Deselect a node.
16452      * @param {TreeNode} node The node to unselect
16453      */
16454     unselect : function(node){
16455         if(this.selNode == node){
16456             this.clearSelections();
16457         }    
16458     },
16459     
16460     /**
16461      * Clear all selections
16462      */
16463     clearSelections : function(){
16464         var n = this.selNode;
16465         if(n){
16466             n.ui.onSelectedChange(false);
16467             this.selNode = null;
16468             this.fireEvent("selectionchange", this, null);
16469         }
16470         return n;
16471     },
16472     
16473     /**
16474      * Get the selected node
16475      * @return {TreeNode} The selected node
16476      */
16477     getSelectedNode : function(){
16478         return this.selNode;    
16479     },
16480     
16481     /**
16482      * Returns true if the node is selected
16483      * @param {TreeNode} node The node to check
16484      * @return {Boolean}
16485      */
16486     isSelected : function(node){
16487         return this.selNode == node;  
16488     },
16489
16490     /**
16491      * Selects the node above the selected node in the tree, intelligently walking the nodes
16492      * @return TreeNode The new selection
16493      */
16494     selectPrevious : function(){
16495         var s = this.selNode || this.lastSelNode;
16496         if(!s){
16497             return null;
16498         }
16499         var ps = s.previousSibling;
16500         if(ps){
16501             if(!ps.isExpanded() || ps.childNodes.length < 1){
16502                 return this.select(ps);
16503             } else{
16504                 var lc = ps.lastChild;
16505                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16506                     lc = lc.lastChild;
16507                 }
16508                 return this.select(lc);
16509             }
16510         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16511             return this.select(s.parentNode);
16512         }
16513         return null;
16514     },
16515
16516     /**
16517      * Selects the node above the selected node in the tree, intelligently walking the nodes
16518      * @return TreeNode The new selection
16519      */
16520     selectNext : function(){
16521         var s = this.selNode || this.lastSelNode;
16522         if(!s){
16523             return null;
16524         }
16525         if(s.firstChild && s.isExpanded()){
16526              return this.select(s.firstChild);
16527          }else if(s.nextSibling){
16528              return this.select(s.nextSibling);
16529          }else if(s.parentNode){
16530             var newS = null;
16531             s.parentNode.bubble(function(){
16532                 if(this.nextSibling){
16533                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16534                     return false;
16535                 }
16536             });
16537             return newS;
16538          }
16539         return null;
16540     },
16541
16542     onKeyDown : function(e){
16543         var s = this.selNode || this.lastSelNode;
16544         // undesirable, but required
16545         var sm = this;
16546         if(!s){
16547             return;
16548         }
16549         var k = e.getKey();
16550         switch(k){
16551              case e.DOWN:
16552                  e.stopEvent();
16553                  this.selectNext();
16554              break;
16555              case e.UP:
16556                  e.stopEvent();
16557                  this.selectPrevious();
16558              break;
16559              case e.RIGHT:
16560                  e.preventDefault();
16561                  if(s.hasChildNodes()){
16562                      if(!s.isExpanded()){
16563                          s.expand();
16564                      }else if(s.firstChild){
16565                          this.select(s.firstChild, e);
16566                      }
16567                  }
16568              break;
16569              case e.LEFT:
16570                  e.preventDefault();
16571                  if(s.hasChildNodes() && s.isExpanded()){
16572                      s.collapse();
16573                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16574                      this.select(s.parentNode, e);
16575                  }
16576              break;
16577         };
16578     }
16579 });
16580
16581 /**
16582  * @class Roo.tree.MultiSelectionModel
16583  * @extends Roo.util.Observable
16584  * Multi selection for a TreePanel.
16585  * @param {Object} cfg Configuration
16586  */
16587 Roo.tree.MultiSelectionModel = function(){
16588    this.selNodes = [];
16589    this.selMap = {};
16590    this.addEvents({
16591        /**
16592         * @event selectionchange
16593         * Fires when the selected nodes change
16594         * @param {MultiSelectionModel} this
16595         * @param {Array} nodes Array of the selected nodes
16596         */
16597        "selectionchange" : true
16598    });
16599    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16600    
16601 };
16602
16603 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16604     init : function(tree){
16605         this.tree = tree;
16606         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16607         tree.on("click", this.onNodeClick, this);
16608     },
16609     
16610     onNodeClick : function(node, e){
16611         this.select(node, e, e.ctrlKey);
16612     },
16613     
16614     /**
16615      * Select a node.
16616      * @param {TreeNode} node The node to select
16617      * @param {EventObject} e (optional) An event associated with the selection
16618      * @param {Boolean} keepExisting True to retain existing selections
16619      * @return {TreeNode} The selected node
16620      */
16621     select : function(node, e, keepExisting){
16622         if(keepExisting !== true){
16623             this.clearSelections(true);
16624         }
16625         if(this.isSelected(node)){
16626             this.lastSelNode = node;
16627             return node;
16628         }
16629         this.selNodes.push(node);
16630         this.selMap[node.id] = node;
16631         this.lastSelNode = node;
16632         node.ui.onSelectedChange(true);
16633         this.fireEvent("selectionchange", this, this.selNodes);
16634         return node;
16635     },
16636     
16637     /**
16638      * Deselect a node.
16639      * @param {TreeNode} node The node to unselect
16640      */
16641     unselect : function(node){
16642         if(this.selMap[node.id]){
16643             node.ui.onSelectedChange(false);
16644             var sn = this.selNodes;
16645             var index = -1;
16646             if(sn.indexOf){
16647                 index = sn.indexOf(node);
16648             }else{
16649                 for(var i = 0, len = sn.length; i < len; i++){
16650                     if(sn[i] == node){
16651                         index = i;
16652                         break;
16653                     }
16654                 }
16655             }
16656             if(index != -1){
16657                 this.selNodes.splice(index, 1);
16658             }
16659             delete this.selMap[node.id];
16660             this.fireEvent("selectionchange", this, this.selNodes);
16661         }
16662     },
16663     
16664     /**
16665      * Clear all selections
16666      */
16667     clearSelections : function(suppressEvent){
16668         var sn = this.selNodes;
16669         if(sn.length > 0){
16670             for(var i = 0, len = sn.length; i < len; i++){
16671                 sn[i].ui.onSelectedChange(false);
16672             }
16673             this.selNodes = [];
16674             this.selMap = {};
16675             if(suppressEvent !== true){
16676                 this.fireEvent("selectionchange", this, this.selNodes);
16677             }
16678         }
16679     },
16680     
16681     /**
16682      * Returns true if the node is selected
16683      * @param {TreeNode} node The node to check
16684      * @return {Boolean}
16685      */
16686     isSelected : function(node){
16687         return this.selMap[node.id] ? true : false;  
16688     },
16689     
16690     /**
16691      * Returns an array of the selected nodes
16692      * @return {Array}
16693      */
16694     getSelectedNodes : function(){
16695         return this.selNodes;    
16696     },
16697
16698     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16699
16700     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16701
16702     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16703 });/*
16704  * Based on:
16705  * Ext JS Library 1.1.1
16706  * Copyright(c) 2006-2007, Ext JS, LLC.
16707  *
16708  * Originally Released Under LGPL - original licence link has changed is not relivant.
16709  *
16710  * Fork - LGPL
16711  * <script type="text/javascript">
16712  */
16713  
16714 /**
16715  * @class Roo.tree.TreeNode
16716  * @extends Roo.data.Node
16717  * @cfg {String} text The text for this node
16718  * @cfg {Boolean} expanded true to start the node expanded
16719  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16720  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16721  * @cfg {Boolean} disabled true to start the node disabled
16722  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16723  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16724  * @cfg {String} cls A css class to be added to the node
16725  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16726  * @cfg {String} href URL of the link used for the node (defaults to #)
16727  * @cfg {String} hrefTarget target frame for the link
16728  * @cfg {String} qtip An Ext QuickTip for the node
16729  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16730  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16731  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16732  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16733  * (defaults to undefined with no checkbox rendered)
16734  * @constructor
16735  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16736  */
16737 Roo.tree.TreeNode = function(attributes){
16738     attributes = attributes || {};
16739     if(typeof attributes == "string"){
16740         attributes = {text: attributes};
16741     }
16742     this.childrenRendered = false;
16743     this.rendered = false;
16744     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16745     this.expanded = attributes.expanded === true;
16746     this.isTarget = attributes.isTarget !== false;
16747     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16748     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16749
16750     /**
16751      * Read-only. The text for this node. To change it use setText().
16752      * @type String
16753      */
16754     this.text = attributes.text;
16755     /**
16756      * True if this node is disabled.
16757      * @type Boolean
16758      */
16759     this.disabled = attributes.disabled === true;
16760
16761     this.addEvents({
16762         /**
16763         * @event textchange
16764         * Fires when the text for this node is changed
16765         * @param {Node} this This node
16766         * @param {String} text The new text
16767         * @param {String} oldText The old text
16768         */
16769         "textchange" : true,
16770         /**
16771         * @event beforeexpand
16772         * Fires before this node is expanded, return false to cancel.
16773         * @param {Node} this This node
16774         * @param {Boolean} deep
16775         * @param {Boolean} anim
16776         */
16777         "beforeexpand" : true,
16778         /**
16779         * @event beforecollapse
16780         * Fires before this node is collapsed, return false to cancel.
16781         * @param {Node} this This node
16782         * @param {Boolean} deep
16783         * @param {Boolean} anim
16784         */
16785         "beforecollapse" : true,
16786         /**
16787         * @event expand
16788         * Fires when this node is expanded
16789         * @param {Node} this This node
16790         */
16791         "expand" : true,
16792         /**
16793         * @event disabledchange
16794         * Fires when the disabled status of this node changes
16795         * @param {Node} this This node
16796         * @param {Boolean} disabled
16797         */
16798         "disabledchange" : true,
16799         /**
16800         * @event collapse
16801         * Fires when this node is collapsed
16802         * @param {Node} this This node
16803         */
16804         "collapse" : true,
16805         /**
16806         * @event beforeclick
16807         * Fires before click processing. Return false to cancel the default action.
16808         * @param {Node} this This node
16809         * @param {Roo.EventObject} e The event object
16810         */
16811         "beforeclick":true,
16812         /**
16813         * @event checkchange
16814         * Fires when a node with a checkbox's checked property changes
16815         * @param {Node} this This node
16816         * @param {Boolean} checked
16817         */
16818         "checkchange":true,
16819         /**
16820         * @event click
16821         * Fires when this node is clicked
16822         * @param {Node} this This node
16823         * @param {Roo.EventObject} e The event object
16824         */
16825         "click":true,
16826         /**
16827         * @event dblclick
16828         * Fires when this node is double clicked
16829         * @param {Node} this This node
16830         * @param {Roo.EventObject} e The event object
16831         */
16832         "dblclick":true,
16833         /**
16834         * @event contextmenu
16835         * Fires when this node is right clicked
16836         * @param {Node} this This node
16837         * @param {Roo.EventObject} e The event object
16838         */
16839         "contextmenu":true,
16840         /**
16841         * @event beforechildrenrendered
16842         * Fires right before the child nodes for this node are rendered
16843         * @param {Node} this This node
16844         */
16845         "beforechildrenrendered":true
16846     });
16847
16848     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16849
16850     /**
16851      * Read-only. The UI for this node
16852      * @type TreeNodeUI
16853      */
16854     this.ui = new uiClass(this);
16855     
16856     // finally support items[]
16857     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16858         return;
16859     }
16860     
16861     
16862     Roo.each(this.attributes.items, function(c) {
16863         this.appendChild(Roo.factory(c,Roo.Tree));
16864     }, this);
16865     delete this.attributes.items;
16866     
16867     
16868     
16869 };
16870 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16871     preventHScroll: true,
16872     /**
16873      * Returns true if this node is expanded
16874      * @return {Boolean}
16875      */
16876     isExpanded : function(){
16877         return this.expanded;
16878     },
16879
16880     /**
16881      * Returns the UI object for this node
16882      * @return {TreeNodeUI}
16883      */
16884     getUI : function(){
16885         return this.ui;
16886     },
16887
16888     // private override
16889     setFirstChild : function(node){
16890         var of = this.firstChild;
16891         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16892         if(this.childrenRendered && of && node != of){
16893             of.renderIndent(true, true);
16894         }
16895         if(this.rendered){
16896             this.renderIndent(true, true);
16897         }
16898     },
16899
16900     // private override
16901     setLastChild : function(node){
16902         var ol = this.lastChild;
16903         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16904         if(this.childrenRendered && ol && node != ol){
16905             ol.renderIndent(true, true);
16906         }
16907         if(this.rendered){
16908             this.renderIndent(true, true);
16909         }
16910     },
16911
16912     // these methods are overridden to provide lazy rendering support
16913     // private override
16914     appendChild : function()
16915     {
16916         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16917         if(node && this.childrenRendered){
16918             node.render();
16919         }
16920         this.ui.updateExpandIcon();
16921         return node;
16922     },
16923
16924     // private override
16925     removeChild : function(node){
16926         this.ownerTree.getSelectionModel().unselect(node);
16927         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16928         // if it's been rendered remove dom node
16929         if(this.childrenRendered){
16930             node.ui.remove();
16931         }
16932         if(this.childNodes.length < 1){
16933             this.collapse(false, false);
16934         }else{
16935             this.ui.updateExpandIcon();
16936         }
16937         if(!this.firstChild) {
16938             this.childrenRendered = false;
16939         }
16940         return node;
16941     },
16942
16943     // private override
16944     insertBefore : function(node, refNode){
16945         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16946         if(newNode && refNode && this.childrenRendered){
16947             node.render();
16948         }
16949         this.ui.updateExpandIcon();
16950         return newNode;
16951     },
16952
16953     /**
16954      * Sets the text for this node
16955      * @param {String} text
16956      */
16957     setText : function(text){
16958         var oldText = this.text;
16959         this.text = text;
16960         this.attributes.text = text;
16961         if(this.rendered){ // event without subscribing
16962             this.ui.onTextChange(this, text, oldText);
16963         }
16964         this.fireEvent("textchange", this, text, oldText);
16965     },
16966
16967     /**
16968      * Triggers selection of this node
16969      */
16970     select : function(){
16971         this.getOwnerTree().getSelectionModel().select(this);
16972     },
16973
16974     /**
16975      * Triggers deselection of this node
16976      */
16977     unselect : function(){
16978         this.getOwnerTree().getSelectionModel().unselect(this);
16979     },
16980
16981     /**
16982      * Returns true if this node is selected
16983      * @return {Boolean}
16984      */
16985     isSelected : function(){
16986         return this.getOwnerTree().getSelectionModel().isSelected(this);
16987     },
16988
16989     /**
16990      * Expand this node.
16991      * @param {Boolean} deep (optional) True to expand all children as well
16992      * @param {Boolean} anim (optional) false to cancel the default animation
16993      * @param {Function} callback (optional) A callback to be called when
16994      * expanding this node completes (does not wait for deep expand to complete).
16995      * Called with 1 parameter, this node.
16996      */
16997     expand : function(deep, anim, callback){
16998         if(!this.expanded){
16999             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17000                 return;
17001             }
17002             if(!this.childrenRendered){
17003                 this.renderChildren();
17004             }
17005             this.expanded = true;
17006             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17007                 this.ui.animExpand(function(){
17008                     this.fireEvent("expand", this);
17009                     if(typeof callback == "function"){
17010                         callback(this);
17011                     }
17012                     if(deep === true){
17013                         this.expandChildNodes(true);
17014                     }
17015                 }.createDelegate(this));
17016                 return;
17017             }else{
17018                 this.ui.expand();
17019                 this.fireEvent("expand", this);
17020                 if(typeof callback == "function"){
17021                     callback(this);
17022                 }
17023             }
17024         }else{
17025            if(typeof callback == "function"){
17026                callback(this);
17027            }
17028         }
17029         if(deep === true){
17030             this.expandChildNodes(true);
17031         }
17032     },
17033
17034     isHiddenRoot : function(){
17035         return this.isRoot && !this.getOwnerTree().rootVisible;
17036     },
17037
17038     /**
17039      * Collapse this node.
17040      * @param {Boolean} deep (optional) True to collapse all children as well
17041      * @param {Boolean} anim (optional) false to cancel the default animation
17042      */
17043     collapse : function(deep, anim){
17044         if(this.expanded && !this.isHiddenRoot()){
17045             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17046                 return;
17047             }
17048             this.expanded = false;
17049             if((this.getOwnerTree().animate && anim !== false) || anim){
17050                 this.ui.animCollapse(function(){
17051                     this.fireEvent("collapse", this);
17052                     if(deep === true){
17053                         this.collapseChildNodes(true);
17054                     }
17055                 }.createDelegate(this));
17056                 return;
17057             }else{
17058                 this.ui.collapse();
17059                 this.fireEvent("collapse", this);
17060             }
17061         }
17062         if(deep === true){
17063             var cs = this.childNodes;
17064             for(var i = 0, len = cs.length; i < len; i++) {
17065                 cs[i].collapse(true, false);
17066             }
17067         }
17068     },
17069
17070     // private
17071     delayedExpand : function(delay){
17072         if(!this.expandProcId){
17073             this.expandProcId = this.expand.defer(delay, this);
17074         }
17075     },
17076
17077     // private
17078     cancelExpand : function(){
17079         if(this.expandProcId){
17080             clearTimeout(this.expandProcId);
17081         }
17082         this.expandProcId = false;
17083     },
17084
17085     /**
17086      * Toggles expanded/collapsed state of the node
17087      */
17088     toggle : function(){
17089         if(this.expanded){
17090             this.collapse();
17091         }else{
17092             this.expand();
17093         }
17094     },
17095
17096     /**
17097      * Ensures all parent nodes are expanded
17098      */
17099     ensureVisible : function(callback){
17100         var tree = this.getOwnerTree();
17101         tree.expandPath(this.parentNode.getPath(), false, function(){
17102             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17103             Roo.callback(callback);
17104         }.createDelegate(this));
17105     },
17106
17107     /**
17108      * Expand all child nodes
17109      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17110      */
17111     expandChildNodes : function(deep){
17112         var cs = this.childNodes;
17113         for(var i = 0, len = cs.length; i < len; i++) {
17114                 cs[i].expand(deep);
17115         }
17116     },
17117
17118     /**
17119      * Collapse all child nodes
17120      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17121      */
17122     collapseChildNodes : function(deep){
17123         var cs = this.childNodes;
17124         for(var i = 0, len = cs.length; i < len; i++) {
17125                 cs[i].collapse(deep);
17126         }
17127     },
17128
17129     /**
17130      * Disables this node
17131      */
17132     disable : function(){
17133         this.disabled = true;
17134         this.unselect();
17135         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17136             this.ui.onDisableChange(this, true);
17137         }
17138         this.fireEvent("disabledchange", this, true);
17139     },
17140
17141     /**
17142      * Enables this node
17143      */
17144     enable : function(){
17145         this.disabled = false;
17146         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17147             this.ui.onDisableChange(this, false);
17148         }
17149         this.fireEvent("disabledchange", this, false);
17150     },
17151
17152     // private
17153     renderChildren : function(suppressEvent){
17154         if(suppressEvent !== false){
17155             this.fireEvent("beforechildrenrendered", this);
17156         }
17157         var cs = this.childNodes;
17158         for(var i = 0, len = cs.length; i < len; i++){
17159             cs[i].render(true);
17160         }
17161         this.childrenRendered = true;
17162     },
17163
17164     // private
17165     sort : function(fn, scope){
17166         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17167         if(this.childrenRendered){
17168             var cs = this.childNodes;
17169             for(var i = 0, len = cs.length; i < len; i++){
17170                 cs[i].render(true);
17171             }
17172         }
17173     },
17174
17175     // private
17176     render : function(bulkRender){
17177         this.ui.render(bulkRender);
17178         if(!this.rendered){
17179             this.rendered = true;
17180             if(this.expanded){
17181                 this.expanded = false;
17182                 this.expand(false, false);
17183             }
17184         }
17185     },
17186
17187     // private
17188     renderIndent : function(deep, refresh){
17189         if(refresh){
17190             this.ui.childIndent = null;
17191         }
17192         this.ui.renderIndent();
17193         if(deep === true && this.childrenRendered){
17194             var cs = this.childNodes;
17195             for(var i = 0, len = cs.length; i < len; i++){
17196                 cs[i].renderIndent(true, refresh);
17197             }
17198         }
17199     }
17200 });/*
17201  * Based on:
17202  * Ext JS Library 1.1.1
17203  * Copyright(c) 2006-2007, Ext JS, LLC.
17204  *
17205  * Originally Released Under LGPL - original licence link has changed is not relivant.
17206  *
17207  * Fork - LGPL
17208  * <script type="text/javascript">
17209  */
17210  
17211 /**
17212  * @class Roo.tree.AsyncTreeNode
17213  * @extends Roo.tree.TreeNode
17214  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17215  * @constructor
17216  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17217  */
17218  Roo.tree.AsyncTreeNode = function(config){
17219     this.loaded = false;
17220     this.loading = false;
17221     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17222     /**
17223     * @event beforeload
17224     * Fires before this node is loaded, return false to cancel
17225     * @param {Node} this This node
17226     */
17227     this.addEvents({'beforeload':true, 'load': true});
17228     /**
17229     * @event load
17230     * Fires when this node is loaded
17231     * @param {Node} this This node
17232     */
17233     /**
17234      * The loader used by this node (defaults to using the tree's defined loader)
17235      * @type TreeLoader
17236      * @property loader
17237      */
17238 };
17239 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17240     expand : function(deep, anim, callback){
17241         if(this.loading){ // if an async load is already running, waiting til it's done
17242             var timer;
17243             var f = function(){
17244                 if(!this.loading){ // done loading
17245                     clearInterval(timer);
17246                     this.expand(deep, anim, callback);
17247                 }
17248             }.createDelegate(this);
17249             timer = setInterval(f, 200);
17250             return;
17251         }
17252         if(!this.loaded){
17253             if(this.fireEvent("beforeload", this) === false){
17254                 return;
17255             }
17256             this.loading = true;
17257             this.ui.beforeLoad(this);
17258             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17259             if(loader){
17260                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17261                 return;
17262             }
17263         }
17264         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17265     },
17266     
17267     /**
17268      * Returns true if this node is currently loading
17269      * @return {Boolean}
17270      */
17271     isLoading : function(){
17272         return this.loading;  
17273     },
17274     
17275     loadComplete : function(deep, anim, callback){
17276         this.loading = false;
17277         this.loaded = true;
17278         this.ui.afterLoad(this);
17279         this.fireEvent("load", this);
17280         this.expand(deep, anim, callback);
17281     },
17282     
17283     /**
17284      * Returns true if this node has been loaded
17285      * @return {Boolean}
17286      */
17287     isLoaded : function(){
17288         return this.loaded;
17289     },
17290     
17291     hasChildNodes : function(){
17292         if(!this.isLeaf() && !this.loaded){
17293             return true;
17294         }else{
17295             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17296         }
17297     },
17298
17299     /**
17300      * Trigger a reload for this node
17301      * @param {Function} callback
17302      */
17303     reload : function(callback){
17304         this.collapse(false, false);
17305         while(this.firstChild){
17306             this.removeChild(this.firstChild);
17307         }
17308         this.childrenRendered = false;
17309         this.loaded = false;
17310         if(this.isHiddenRoot()){
17311             this.expanded = false;
17312         }
17313         this.expand(false, false, callback);
17314     }
17315 });/*
17316  * Based on:
17317  * Ext JS Library 1.1.1
17318  * Copyright(c) 2006-2007, Ext JS, LLC.
17319  *
17320  * Originally Released Under LGPL - original licence link has changed is not relivant.
17321  *
17322  * Fork - LGPL
17323  * <script type="text/javascript">
17324  */
17325  
17326 /**
17327  * @class Roo.tree.TreeNodeUI
17328  * @constructor
17329  * @param {Object} node The node to render
17330  * The TreeNode UI implementation is separate from the
17331  * tree implementation. Unless you are customizing the tree UI,
17332  * you should never have to use this directly.
17333  */
17334 Roo.tree.TreeNodeUI = function(node){
17335     this.node = node;
17336     this.rendered = false;
17337     this.animating = false;
17338     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17339 };
17340
17341 Roo.tree.TreeNodeUI.prototype = {
17342     removeChild : function(node){
17343         if(this.rendered){
17344             this.ctNode.removeChild(node.ui.getEl());
17345         }
17346     },
17347
17348     beforeLoad : function(){
17349          this.addClass("x-tree-node-loading");
17350     },
17351
17352     afterLoad : function(){
17353          this.removeClass("x-tree-node-loading");
17354     },
17355
17356     onTextChange : function(node, text, oldText){
17357         if(this.rendered){
17358             this.textNode.innerHTML = text;
17359         }
17360     },
17361
17362     onDisableChange : function(node, state){
17363         this.disabled = state;
17364         if(state){
17365             this.addClass("x-tree-node-disabled");
17366         }else{
17367             this.removeClass("x-tree-node-disabled");
17368         }
17369     },
17370
17371     onSelectedChange : function(state){
17372         if(state){
17373             this.focus();
17374             this.addClass("x-tree-selected");
17375         }else{
17376             //this.blur();
17377             this.removeClass("x-tree-selected");
17378         }
17379     },
17380
17381     onMove : function(tree, node, oldParent, newParent, index, refNode){
17382         this.childIndent = null;
17383         if(this.rendered){
17384             var targetNode = newParent.ui.getContainer();
17385             if(!targetNode){//target not rendered
17386                 this.holder = document.createElement("div");
17387                 this.holder.appendChild(this.wrap);
17388                 return;
17389             }
17390             var insertBefore = refNode ? refNode.ui.getEl() : null;
17391             if(insertBefore){
17392                 targetNode.insertBefore(this.wrap, insertBefore);
17393             }else{
17394                 targetNode.appendChild(this.wrap);
17395             }
17396             this.node.renderIndent(true);
17397         }
17398     },
17399
17400     addClass : function(cls){
17401         if(this.elNode){
17402             Roo.fly(this.elNode).addClass(cls);
17403         }
17404     },
17405
17406     removeClass : function(cls){
17407         if(this.elNode){
17408             Roo.fly(this.elNode).removeClass(cls);
17409         }
17410     },
17411
17412     remove : function(){
17413         if(this.rendered){
17414             this.holder = document.createElement("div");
17415             this.holder.appendChild(this.wrap);
17416         }
17417     },
17418
17419     fireEvent : function(){
17420         return this.node.fireEvent.apply(this.node, arguments);
17421     },
17422
17423     initEvents : function(){
17424         this.node.on("move", this.onMove, this);
17425         var E = Roo.EventManager;
17426         var a = this.anchor;
17427
17428         var el = Roo.fly(a, '_treeui');
17429
17430         if(Roo.isOpera){ // opera render bug ignores the CSS
17431             el.setStyle("text-decoration", "none");
17432         }
17433
17434         el.on("click", this.onClick, this);
17435         el.on("dblclick", this.onDblClick, this);
17436
17437         if(this.checkbox){
17438             Roo.EventManager.on(this.checkbox,
17439                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17440         }
17441
17442         el.on("contextmenu", this.onContextMenu, this);
17443
17444         var icon = Roo.fly(this.iconNode);
17445         icon.on("click", this.onClick, this);
17446         icon.on("dblclick", this.onDblClick, this);
17447         icon.on("contextmenu", this.onContextMenu, this);
17448         E.on(this.ecNode, "click", this.ecClick, this, true);
17449
17450         if(this.node.disabled){
17451             this.addClass("x-tree-node-disabled");
17452         }
17453         if(this.node.hidden){
17454             this.addClass("x-tree-node-disabled");
17455         }
17456         var ot = this.node.getOwnerTree();
17457         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17458         if(dd && (!this.node.isRoot || ot.rootVisible)){
17459             Roo.dd.Registry.register(this.elNode, {
17460                 node: this.node,
17461                 handles: this.getDDHandles(),
17462                 isHandle: false
17463             });
17464         }
17465     },
17466
17467     getDDHandles : function(){
17468         return [this.iconNode, this.textNode];
17469     },
17470
17471     hide : function(){
17472         if(this.rendered){
17473             this.wrap.style.display = "none";
17474         }
17475     },
17476
17477     show : function(){
17478         if(this.rendered){
17479             this.wrap.style.display = "";
17480         }
17481     },
17482
17483     onContextMenu : function(e){
17484         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17485             e.preventDefault();
17486             this.focus();
17487             this.fireEvent("contextmenu", this.node, e);
17488         }
17489     },
17490
17491     onClick : function(e){
17492         if(this.dropping){
17493             e.stopEvent();
17494             return;
17495         }
17496         if(this.fireEvent("beforeclick", this.node, e) !== false){
17497             if(!this.disabled && this.node.attributes.href){
17498                 this.fireEvent("click", this.node, e);
17499                 return;
17500             }
17501             e.preventDefault();
17502             if(this.disabled){
17503                 return;
17504             }
17505
17506             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17507                 this.node.toggle();
17508             }
17509
17510             this.fireEvent("click", this.node, e);
17511         }else{
17512             e.stopEvent();
17513         }
17514     },
17515
17516     onDblClick : function(e){
17517         e.preventDefault();
17518         if(this.disabled){
17519             return;
17520         }
17521         if(this.checkbox){
17522             this.toggleCheck();
17523         }
17524         if(!this.animating && this.node.hasChildNodes()){
17525             this.node.toggle();
17526         }
17527         this.fireEvent("dblclick", this.node, e);
17528     },
17529
17530     onCheckChange : function(){
17531         var checked = this.checkbox.checked;
17532         this.node.attributes.checked = checked;
17533         this.fireEvent('checkchange', this.node, checked);
17534     },
17535
17536     ecClick : function(e){
17537         if(!this.animating && this.node.hasChildNodes()){
17538             this.node.toggle();
17539         }
17540     },
17541
17542     startDrop : function(){
17543         this.dropping = true;
17544     },
17545
17546     // delayed drop so the click event doesn't get fired on a drop
17547     endDrop : function(){
17548        setTimeout(function(){
17549            this.dropping = false;
17550        }.createDelegate(this), 50);
17551     },
17552
17553     expand : function(){
17554         this.updateExpandIcon();
17555         this.ctNode.style.display = "";
17556     },
17557
17558     focus : function(){
17559         if(!this.node.preventHScroll){
17560             try{this.anchor.focus();
17561             }catch(e){}
17562         }else if(!Roo.isIE){
17563             try{
17564                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17565                 var l = noscroll.scrollLeft;
17566                 this.anchor.focus();
17567                 noscroll.scrollLeft = l;
17568             }catch(e){}
17569         }
17570     },
17571
17572     toggleCheck : function(value){
17573         var cb = this.checkbox;
17574         if(cb){
17575             cb.checked = (value === undefined ? !cb.checked : value);
17576         }
17577     },
17578
17579     blur : function(){
17580         try{
17581             this.anchor.blur();
17582         }catch(e){}
17583     },
17584
17585     animExpand : function(callback){
17586         var ct = Roo.get(this.ctNode);
17587         ct.stopFx();
17588         if(!this.node.hasChildNodes()){
17589             this.updateExpandIcon();
17590             this.ctNode.style.display = "";
17591             Roo.callback(callback);
17592             return;
17593         }
17594         this.animating = true;
17595         this.updateExpandIcon();
17596
17597         ct.slideIn('t', {
17598            callback : function(){
17599                this.animating = false;
17600                Roo.callback(callback);
17601             },
17602             scope: this,
17603             duration: this.node.ownerTree.duration || .25
17604         });
17605     },
17606
17607     highlight : function(){
17608         var tree = this.node.getOwnerTree();
17609         Roo.fly(this.wrap).highlight(
17610             tree.hlColor || "C3DAF9",
17611             {endColor: tree.hlBaseColor}
17612         );
17613     },
17614
17615     collapse : function(){
17616         this.updateExpandIcon();
17617         this.ctNode.style.display = "none";
17618     },
17619
17620     animCollapse : function(callback){
17621         var ct = Roo.get(this.ctNode);
17622         ct.enableDisplayMode('block');
17623         ct.stopFx();
17624
17625         this.animating = true;
17626         this.updateExpandIcon();
17627
17628         ct.slideOut('t', {
17629             callback : function(){
17630                this.animating = false;
17631                Roo.callback(callback);
17632             },
17633             scope: this,
17634             duration: this.node.ownerTree.duration || .25
17635         });
17636     },
17637
17638     getContainer : function(){
17639         return this.ctNode;
17640     },
17641
17642     getEl : function(){
17643         return this.wrap;
17644     },
17645
17646     appendDDGhost : function(ghostNode){
17647         ghostNode.appendChild(this.elNode.cloneNode(true));
17648     },
17649
17650     getDDRepairXY : function(){
17651         return Roo.lib.Dom.getXY(this.iconNode);
17652     },
17653
17654     onRender : function(){
17655         this.render();
17656     },
17657
17658     render : function(bulkRender){
17659         var n = this.node, a = n.attributes;
17660         var targetNode = n.parentNode ?
17661               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17662
17663         if(!this.rendered){
17664             this.rendered = true;
17665
17666             this.renderElements(n, a, targetNode, bulkRender);
17667
17668             if(a.qtip){
17669                if(this.textNode.setAttributeNS){
17670                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17671                    if(a.qtipTitle){
17672                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17673                    }
17674                }else{
17675                    this.textNode.setAttribute("ext:qtip", a.qtip);
17676                    if(a.qtipTitle){
17677                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17678                    }
17679                }
17680             }else if(a.qtipCfg){
17681                 a.qtipCfg.target = Roo.id(this.textNode);
17682                 Roo.QuickTips.register(a.qtipCfg);
17683             }
17684             this.initEvents();
17685             if(!this.node.expanded){
17686                 this.updateExpandIcon();
17687             }
17688         }else{
17689             if(bulkRender === true) {
17690                 targetNode.appendChild(this.wrap);
17691             }
17692         }
17693     },
17694
17695     renderElements : function(n, a, targetNode, bulkRender)
17696     {
17697         // add some indent caching, this helps performance when rendering a large tree
17698         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17699         var t = n.getOwnerTree();
17700         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17701         if (typeof(n.attributes.html) != 'undefined') {
17702             txt = n.attributes.html;
17703         }
17704         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17705         var cb = typeof a.checked == 'boolean';
17706         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17707         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17708             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17709             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17710             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17711             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17712             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17713              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17714                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17715             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17716             "</li>"];
17717
17718         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17719             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17720                                 n.nextSibling.ui.getEl(), buf.join(""));
17721         }else{
17722             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17723         }
17724
17725         this.elNode = this.wrap.childNodes[0];
17726         this.ctNode = this.wrap.childNodes[1];
17727         var cs = this.elNode.childNodes;
17728         this.indentNode = cs[0];
17729         this.ecNode = cs[1];
17730         this.iconNode = cs[2];
17731         var index = 3;
17732         if(cb){
17733             this.checkbox = cs[3];
17734             index++;
17735         }
17736         this.anchor = cs[index];
17737         this.textNode = cs[index].firstChild;
17738     },
17739
17740     getAnchor : function(){
17741         return this.anchor;
17742     },
17743
17744     getTextEl : function(){
17745         return this.textNode;
17746     },
17747
17748     getIconEl : function(){
17749         return this.iconNode;
17750     },
17751
17752     isChecked : function(){
17753         return this.checkbox ? this.checkbox.checked : false;
17754     },
17755
17756     updateExpandIcon : function(){
17757         if(this.rendered){
17758             var n = this.node, c1, c2;
17759             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17760             var hasChild = n.hasChildNodes();
17761             if(hasChild){
17762                 if(n.expanded){
17763                     cls += "-minus";
17764                     c1 = "x-tree-node-collapsed";
17765                     c2 = "x-tree-node-expanded";
17766                 }else{
17767                     cls += "-plus";
17768                     c1 = "x-tree-node-expanded";
17769                     c2 = "x-tree-node-collapsed";
17770                 }
17771                 if(this.wasLeaf){
17772                     this.removeClass("x-tree-node-leaf");
17773                     this.wasLeaf = false;
17774                 }
17775                 if(this.c1 != c1 || this.c2 != c2){
17776                     Roo.fly(this.elNode).replaceClass(c1, c2);
17777                     this.c1 = c1; this.c2 = c2;
17778                 }
17779             }else{
17780                 // this changes non-leafs into leafs if they have no children.
17781                 // it's not very rational behaviour..
17782                 
17783                 if(!this.wasLeaf && this.node.leaf){
17784                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17785                     delete this.c1;
17786                     delete this.c2;
17787                     this.wasLeaf = true;
17788                 }
17789             }
17790             var ecc = "x-tree-ec-icon "+cls;
17791             if(this.ecc != ecc){
17792                 this.ecNode.className = ecc;
17793                 this.ecc = ecc;
17794             }
17795         }
17796     },
17797
17798     getChildIndent : function(){
17799         if(!this.childIndent){
17800             var buf = [];
17801             var p = this.node;
17802             while(p){
17803                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17804                     if(!p.isLast()) {
17805                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17806                     } else {
17807                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17808                     }
17809                 }
17810                 p = p.parentNode;
17811             }
17812             this.childIndent = buf.join("");
17813         }
17814         return this.childIndent;
17815     },
17816
17817     renderIndent : function(){
17818         if(this.rendered){
17819             var indent = "";
17820             var p = this.node.parentNode;
17821             if(p){
17822                 indent = p.ui.getChildIndent();
17823             }
17824             if(this.indentMarkup != indent){ // don't rerender if not required
17825                 this.indentNode.innerHTML = indent;
17826                 this.indentMarkup = indent;
17827             }
17828             this.updateExpandIcon();
17829         }
17830     }
17831 };
17832
17833 Roo.tree.RootTreeNodeUI = function(){
17834     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17835 };
17836 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17837     render : function(){
17838         if(!this.rendered){
17839             var targetNode = this.node.ownerTree.innerCt.dom;
17840             this.node.expanded = true;
17841             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17842             this.wrap = this.ctNode = targetNode.firstChild;
17843         }
17844     },
17845     collapse : function(){
17846     },
17847     expand : function(){
17848     }
17849 });/*
17850  * Based on:
17851  * Ext JS Library 1.1.1
17852  * Copyright(c) 2006-2007, Ext JS, LLC.
17853  *
17854  * Originally Released Under LGPL - original licence link has changed is not relivant.
17855  *
17856  * Fork - LGPL
17857  * <script type="text/javascript">
17858  */
17859 /**
17860  * @class Roo.tree.TreeLoader
17861  * @extends Roo.util.Observable
17862  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17863  * nodes from a specified URL. The response must be a javascript Array definition
17864  * who's elements are node definition objects. eg:
17865  * <pre><code>
17866 {  success : true,
17867    data :      [
17868    
17869     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17870     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17871     ]
17872 }
17873
17874
17875 </code></pre>
17876  * <br><br>
17877  * The old style respose with just an array is still supported, but not recommended.
17878  * <br><br>
17879  *
17880  * A server request is sent, and child nodes are loaded only when a node is expanded.
17881  * The loading node's id is passed to the server under the parameter name "node" to
17882  * enable the server to produce the correct child nodes.
17883  * <br><br>
17884  * To pass extra parameters, an event handler may be attached to the "beforeload"
17885  * event, and the parameters specified in the TreeLoader's baseParams property:
17886  * <pre><code>
17887     myTreeLoader.on("beforeload", function(treeLoader, node) {
17888         this.baseParams.category = node.attributes.category;
17889     }, this);
17890 </code></pre><
17891  * This would pass an HTTP parameter called "category" to the server containing
17892  * the value of the Node's "category" attribute.
17893  * @constructor
17894  * Creates a new Treeloader.
17895  * @param {Object} config A config object containing config properties.
17896  */
17897 Roo.tree.TreeLoader = function(config){
17898     this.baseParams = {};
17899     this.requestMethod = "POST";
17900     Roo.apply(this, config);
17901
17902     this.addEvents({
17903     
17904         /**
17905          * @event beforeload
17906          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17907          * @param {Object} This TreeLoader object.
17908          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17909          * @param {Object} callback The callback function specified in the {@link #load} call.
17910          */
17911         beforeload : true,
17912         /**
17913          * @event load
17914          * Fires when the node has been successfuly loaded.
17915          * @param {Object} This TreeLoader object.
17916          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17917          * @param {Object} response The response object containing the data from the server.
17918          */
17919         load : true,
17920         /**
17921          * @event loadexception
17922          * Fires if the network request failed.
17923          * @param {Object} This TreeLoader object.
17924          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17925          * @param {Object} response The response object containing the data from the server.
17926          */
17927         loadexception : true,
17928         /**
17929          * @event create
17930          * Fires before a node is created, enabling you to return custom Node types 
17931          * @param {Object} This TreeLoader object.
17932          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17933          */
17934         create : true
17935     });
17936
17937     Roo.tree.TreeLoader.superclass.constructor.call(this);
17938 };
17939
17940 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17941     /**
17942     * @cfg {String} dataUrl The URL from which to request a Json string which
17943     * specifies an array of node definition object representing the child nodes
17944     * to be loaded.
17945     */
17946     /**
17947     * @cfg {String} requestMethod either GET or POST
17948     * defaults to POST (due to BC)
17949     * to be loaded.
17950     */
17951     /**
17952     * @cfg {Object} baseParams (optional) An object containing properties which
17953     * specify HTTP parameters to be passed to each request for child nodes.
17954     */
17955     /**
17956     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17957     * created by this loader. If the attributes sent by the server have an attribute in this object,
17958     * they take priority.
17959     */
17960     /**
17961     * @cfg {Object} uiProviders (optional) An object containing properties which
17962     * 
17963     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17964     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17965     * <i>uiProvider</i> attribute of a returned child node is a string rather
17966     * than a reference to a TreeNodeUI implementation, this that string value
17967     * is used as a property name in the uiProviders object. You can define the provider named
17968     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
17969     */
17970     uiProviders : {},
17971
17972     /**
17973     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
17974     * child nodes before loading.
17975     */
17976     clearOnLoad : true,
17977
17978     /**
17979     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
17980     * property on loading, rather than expecting an array. (eg. more compatible to a standard
17981     * Grid query { data : [ .....] }
17982     */
17983     
17984     root : false,
17985      /**
17986     * @cfg {String} queryParam (optional) 
17987     * Name of the query as it will be passed on the querystring (defaults to 'node')
17988     * eg. the request will be ?node=[id]
17989     */
17990     
17991     
17992     queryParam: false,
17993     
17994     /**
17995      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
17996      * This is called automatically when a node is expanded, but may be used to reload
17997      * a node (or append new children if the {@link #clearOnLoad} option is false.)
17998      * @param {Roo.tree.TreeNode} node
17999      * @param {Function} callback
18000      */
18001     load : function(node, callback){
18002         if(this.clearOnLoad){
18003             while(node.firstChild){
18004                 node.removeChild(node.firstChild);
18005             }
18006         }
18007         if(node.attributes.children){ // preloaded json children
18008             var cs = node.attributes.children;
18009             for(var i = 0, len = cs.length; i < len; i++){
18010                 node.appendChild(this.createNode(cs[i]));
18011             }
18012             if(typeof callback == "function"){
18013                 callback();
18014             }
18015         }else if(this.dataUrl){
18016             this.requestData(node, callback);
18017         }
18018     },
18019
18020     getParams: function(node){
18021         var buf = [], bp = this.baseParams;
18022         for(var key in bp){
18023             if(typeof bp[key] != "function"){
18024                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18025             }
18026         }
18027         var n = this.queryParam === false ? 'node' : this.queryParam;
18028         buf.push(n + "=", encodeURIComponent(node.id));
18029         return buf.join("");
18030     },
18031
18032     requestData : function(node, callback){
18033         if(this.fireEvent("beforeload", this, node, callback) !== false){
18034             this.transId = Roo.Ajax.request({
18035                 method:this.requestMethod,
18036                 url: this.dataUrl||this.url,
18037                 success: this.handleResponse,
18038                 failure: this.handleFailure,
18039                 scope: this,
18040                 argument: {callback: callback, node: node},
18041                 params: this.getParams(node)
18042             });
18043         }else{
18044             // if the load is cancelled, make sure we notify
18045             // the node that we are done
18046             if(typeof callback == "function"){
18047                 callback();
18048             }
18049         }
18050     },
18051
18052     isLoading : function(){
18053         return this.transId ? true : false;
18054     },
18055
18056     abort : function(){
18057         if(this.isLoading()){
18058             Roo.Ajax.abort(this.transId);
18059         }
18060     },
18061
18062     // private
18063     createNode : function(attr)
18064     {
18065         // apply baseAttrs, nice idea Corey!
18066         if(this.baseAttrs){
18067             Roo.applyIf(attr, this.baseAttrs);
18068         }
18069         if(this.applyLoader !== false){
18070             attr.loader = this;
18071         }
18072         // uiProvider = depreciated..
18073         
18074         if(typeof(attr.uiProvider) == 'string'){
18075            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18076                 /**  eval:var:attr */ eval(attr.uiProvider);
18077         }
18078         if(typeof(this.uiProviders['default']) != 'undefined') {
18079             attr.uiProvider = this.uiProviders['default'];
18080         }
18081         
18082         this.fireEvent('create', this, attr);
18083         
18084         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18085         return(attr.leaf ?
18086                         new Roo.tree.TreeNode(attr) :
18087                         new Roo.tree.AsyncTreeNode(attr));
18088     },
18089
18090     processResponse : function(response, node, callback)
18091     {
18092         var json = response.responseText;
18093         try {
18094             
18095             var o = Roo.decode(json);
18096             
18097             if (this.root === false && typeof(o.success) != undefined) {
18098                 this.root = 'data'; // the default behaviour for list like data..
18099                 }
18100                 
18101             if (this.root !== false &&  !o.success) {
18102                 // it's a failure condition.
18103                 var a = response.argument;
18104                 this.fireEvent("loadexception", this, a.node, response);
18105                 Roo.log("Load failed - should have a handler really");
18106                 return;
18107             }
18108             
18109             
18110             
18111             if (this.root !== false) {
18112                  o = o[this.root];
18113             }
18114             
18115             for(var i = 0, len = o.length; i < len; i++){
18116                 var n = this.createNode(o[i]);
18117                 if(n){
18118                     node.appendChild(n);
18119                 }
18120             }
18121             if(typeof callback == "function"){
18122                 callback(this, node);
18123             }
18124         }catch(e){
18125             this.handleFailure(response);
18126         }
18127     },
18128
18129     handleResponse : function(response){
18130         this.transId = false;
18131         var a = response.argument;
18132         this.processResponse(response, a.node, a.callback);
18133         this.fireEvent("load", this, a.node, response);
18134     },
18135
18136     handleFailure : function(response)
18137     {
18138         // should handle failure better..
18139         this.transId = false;
18140         var a = response.argument;
18141         this.fireEvent("loadexception", this, a.node, response);
18142         if(typeof a.callback == "function"){
18143             a.callback(this, a.node);
18144         }
18145     }
18146 });/*
18147  * Based on:
18148  * Ext JS Library 1.1.1
18149  * Copyright(c) 2006-2007, Ext JS, LLC.
18150  *
18151  * Originally Released Under LGPL - original licence link has changed is not relivant.
18152  *
18153  * Fork - LGPL
18154  * <script type="text/javascript">
18155  */
18156
18157 /**
18158 * @class Roo.tree.TreeFilter
18159 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18160 * @param {TreePanel} tree
18161 * @param {Object} config (optional)
18162  */
18163 Roo.tree.TreeFilter = function(tree, config){
18164     this.tree = tree;
18165     this.filtered = {};
18166     Roo.apply(this, config);
18167 };
18168
18169 Roo.tree.TreeFilter.prototype = {
18170     clearBlank:false,
18171     reverse:false,
18172     autoClear:false,
18173     remove:false,
18174
18175      /**
18176      * Filter the data by a specific attribute.
18177      * @param {String/RegExp} value Either string that the attribute value
18178      * should start with or a RegExp to test against the attribute
18179      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18180      * @param {TreeNode} startNode (optional) The node to start the filter at.
18181      */
18182     filter : function(value, attr, startNode){
18183         attr = attr || "text";
18184         var f;
18185         if(typeof value == "string"){
18186             var vlen = value.length;
18187             // auto clear empty filter
18188             if(vlen == 0 && this.clearBlank){
18189                 this.clear();
18190                 return;
18191             }
18192             value = value.toLowerCase();
18193             f = function(n){
18194                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18195             };
18196         }else if(value.exec){ // regex?
18197             f = function(n){
18198                 return value.test(n.attributes[attr]);
18199             };
18200         }else{
18201             throw 'Illegal filter type, must be string or regex';
18202         }
18203         this.filterBy(f, null, startNode);
18204         },
18205
18206     /**
18207      * Filter by a function. The passed function will be called with each
18208      * node in the tree (or from the startNode). If the function returns true, the node is kept
18209      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18210      * @param {Function} fn The filter function
18211      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18212      */
18213     filterBy : function(fn, scope, startNode){
18214         startNode = startNode || this.tree.root;
18215         if(this.autoClear){
18216             this.clear();
18217         }
18218         var af = this.filtered, rv = this.reverse;
18219         var f = function(n){
18220             if(n == startNode){
18221                 return true;
18222             }
18223             if(af[n.id]){
18224                 return false;
18225             }
18226             var m = fn.call(scope || n, n);
18227             if(!m || rv){
18228                 af[n.id] = n;
18229                 n.ui.hide();
18230                 return false;
18231             }
18232             return true;
18233         };
18234         startNode.cascade(f);
18235         if(this.remove){
18236            for(var id in af){
18237                if(typeof id != "function"){
18238                    var n = af[id];
18239                    if(n && n.parentNode){
18240                        n.parentNode.removeChild(n);
18241                    }
18242                }
18243            }
18244         }
18245     },
18246
18247     /**
18248      * Clears the current filter. Note: with the "remove" option
18249      * set a filter cannot be cleared.
18250      */
18251     clear : function(){
18252         var t = this.tree;
18253         var af = this.filtered;
18254         for(var id in af){
18255             if(typeof id != "function"){
18256                 var n = af[id];
18257                 if(n){
18258                     n.ui.show();
18259                 }
18260             }
18261         }
18262         this.filtered = {};
18263     }
18264 };
18265 /*
18266  * Based on:
18267  * Ext JS Library 1.1.1
18268  * Copyright(c) 2006-2007, Ext JS, LLC.
18269  *
18270  * Originally Released Under LGPL - original licence link has changed is not relivant.
18271  *
18272  * Fork - LGPL
18273  * <script type="text/javascript">
18274  */
18275  
18276
18277 /**
18278  * @class Roo.tree.TreeSorter
18279  * Provides sorting of nodes in a TreePanel
18280  * 
18281  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18282  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18283  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18284  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18285  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18286  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18287  * @constructor
18288  * @param {TreePanel} tree
18289  * @param {Object} config
18290  */
18291 Roo.tree.TreeSorter = function(tree, config){
18292     Roo.apply(this, config);
18293     tree.on("beforechildrenrendered", this.doSort, this);
18294     tree.on("append", this.updateSort, this);
18295     tree.on("insert", this.updateSort, this);
18296     
18297     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18298     var p = this.property || "text";
18299     var sortType = this.sortType;
18300     var fs = this.folderSort;
18301     var cs = this.caseSensitive === true;
18302     var leafAttr = this.leafAttr || 'leaf';
18303
18304     this.sortFn = function(n1, n2){
18305         if(fs){
18306             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18307                 return 1;
18308             }
18309             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18310                 return -1;
18311             }
18312         }
18313         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18314         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18315         if(v1 < v2){
18316                         return dsc ? +1 : -1;
18317                 }else if(v1 > v2){
18318                         return dsc ? -1 : +1;
18319         }else{
18320                 return 0;
18321         }
18322     };
18323 };
18324
18325 Roo.tree.TreeSorter.prototype = {
18326     doSort : function(node){
18327         node.sort(this.sortFn);
18328     },
18329     
18330     compareNodes : function(n1, n2){
18331         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18332     },
18333     
18334     updateSort : function(tree, node){
18335         if(node.childrenRendered){
18336             this.doSort.defer(1, this, [node]);
18337         }
18338     }
18339 };/*
18340  * Based on:
18341  * Ext JS Library 1.1.1
18342  * Copyright(c) 2006-2007, Ext JS, LLC.
18343  *
18344  * Originally Released Under LGPL - original licence link has changed is not relivant.
18345  *
18346  * Fork - LGPL
18347  * <script type="text/javascript">
18348  */
18349
18350 if(Roo.dd.DropZone){
18351     
18352 Roo.tree.TreeDropZone = function(tree, config){
18353     this.allowParentInsert = false;
18354     this.allowContainerDrop = false;
18355     this.appendOnly = false;
18356     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18357     this.tree = tree;
18358     this.lastInsertClass = "x-tree-no-status";
18359     this.dragOverData = {};
18360 };
18361
18362 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18363     ddGroup : "TreeDD",
18364     scroll:  true,
18365     
18366     expandDelay : 1000,
18367     
18368     expandNode : function(node){
18369         if(node.hasChildNodes() && !node.isExpanded()){
18370             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18371         }
18372     },
18373     
18374     queueExpand : function(node){
18375         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18376     },
18377     
18378     cancelExpand : function(){
18379         if(this.expandProcId){
18380             clearTimeout(this.expandProcId);
18381             this.expandProcId = false;
18382         }
18383     },
18384     
18385     isValidDropPoint : function(n, pt, dd, e, data){
18386         if(!n || !data){ return false; }
18387         var targetNode = n.node;
18388         var dropNode = data.node;
18389         // default drop rules
18390         if(!(targetNode && targetNode.isTarget && pt)){
18391             return false;
18392         }
18393         if(pt == "append" && targetNode.allowChildren === false){
18394             return false;
18395         }
18396         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18397             return false;
18398         }
18399         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18400             return false;
18401         }
18402         // reuse the object
18403         var overEvent = this.dragOverData;
18404         overEvent.tree = this.tree;
18405         overEvent.target = targetNode;
18406         overEvent.data = data;
18407         overEvent.point = pt;
18408         overEvent.source = dd;
18409         overEvent.rawEvent = e;
18410         overEvent.dropNode = dropNode;
18411         overEvent.cancel = false;  
18412         var result = this.tree.fireEvent("nodedragover", overEvent);
18413         return overEvent.cancel === false && result !== false;
18414     },
18415     
18416     getDropPoint : function(e, n, dd)
18417     {
18418         var tn = n.node;
18419         if(tn.isRoot){
18420             return tn.allowChildren !== false ? "append" : false; // always append for root
18421         }
18422         var dragEl = n.ddel;
18423         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18424         var y = Roo.lib.Event.getPageY(e);
18425         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18426         
18427         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18428         var noAppend = tn.allowChildren === false;
18429         if(this.appendOnly || tn.parentNode.allowChildren === false){
18430             return noAppend ? false : "append";
18431         }
18432         var noBelow = false;
18433         if(!this.allowParentInsert){
18434             noBelow = tn.hasChildNodes() && tn.isExpanded();
18435         }
18436         var q = (b - t) / (noAppend ? 2 : 3);
18437         if(y >= t && y < (t + q)){
18438             return "above";
18439         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18440             return "below";
18441         }else{
18442             return "append";
18443         }
18444     },
18445     
18446     onNodeEnter : function(n, dd, e, data)
18447     {
18448         this.cancelExpand();
18449     },
18450     
18451     onNodeOver : function(n, dd, e, data)
18452     {
18453        
18454         var pt = this.getDropPoint(e, n, dd);
18455         var node = n.node;
18456         
18457         // auto node expand check
18458         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18459             this.queueExpand(node);
18460         }else if(pt != "append"){
18461             this.cancelExpand();
18462         }
18463         
18464         // set the insert point style on the target node
18465         var returnCls = this.dropNotAllowed;
18466         if(this.isValidDropPoint(n, pt, dd, e, data)){
18467            if(pt){
18468                var el = n.ddel;
18469                var cls;
18470                if(pt == "above"){
18471                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18472                    cls = "x-tree-drag-insert-above";
18473                }else if(pt == "below"){
18474                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18475                    cls = "x-tree-drag-insert-below";
18476                }else{
18477                    returnCls = "x-tree-drop-ok-append";
18478                    cls = "x-tree-drag-append";
18479                }
18480                if(this.lastInsertClass != cls){
18481                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18482                    this.lastInsertClass = cls;
18483                }
18484            }
18485        }
18486        return returnCls;
18487     },
18488     
18489     onNodeOut : function(n, dd, e, data){
18490         
18491         this.cancelExpand();
18492         this.removeDropIndicators(n);
18493     },
18494     
18495     onNodeDrop : function(n, dd, e, data){
18496         var point = this.getDropPoint(e, n, dd);
18497         var targetNode = n.node;
18498         targetNode.ui.startDrop();
18499         if(!this.isValidDropPoint(n, point, dd, e, data)){
18500             targetNode.ui.endDrop();
18501             return false;
18502         }
18503         // first try to find the drop node
18504         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18505         var dropEvent = {
18506             tree : this.tree,
18507             target: targetNode,
18508             data: data,
18509             point: point,
18510             source: dd,
18511             rawEvent: e,
18512             dropNode: dropNode,
18513             cancel: !dropNode   
18514         };
18515         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18516         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18517             targetNode.ui.endDrop();
18518             return false;
18519         }
18520         // allow target changing
18521         targetNode = dropEvent.target;
18522         if(point == "append" && !targetNode.isExpanded()){
18523             targetNode.expand(false, null, function(){
18524                 this.completeDrop(dropEvent);
18525             }.createDelegate(this));
18526         }else{
18527             this.completeDrop(dropEvent);
18528         }
18529         return true;
18530     },
18531     
18532     completeDrop : function(de){
18533         var ns = de.dropNode, p = de.point, t = de.target;
18534         if(!(ns instanceof Array)){
18535             ns = [ns];
18536         }
18537         var n;
18538         for(var i = 0, len = ns.length; i < len; i++){
18539             n = ns[i];
18540             if(p == "above"){
18541                 t.parentNode.insertBefore(n, t);
18542             }else if(p == "below"){
18543                 t.parentNode.insertBefore(n, t.nextSibling);
18544             }else{
18545                 t.appendChild(n);
18546             }
18547         }
18548         n.ui.focus();
18549         if(this.tree.hlDrop){
18550             n.ui.highlight();
18551         }
18552         t.ui.endDrop();
18553         this.tree.fireEvent("nodedrop", de);
18554     },
18555     
18556     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18557         if(this.tree.hlDrop){
18558             dropNode.ui.focus();
18559             dropNode.ui.highlight();
18560         }
18561         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18562     },
18563     
18564     getTree : function(){
18565         return this.tree;
18566     },
18567     
18568     removeDropIndicators : function(n){
18569         if(n && n.ddel){
18570             var el = n.ddel;
18571             Roo.fly(el).removeClass([
18572                     "x-tree-drag-insert-above",
18573                     "x-tree-drag-insert-below",
18574                     "x-tree-drag-append"]);
18575             this.lastInsertClass = "_noclass";
18576         }
18577     },
18578     
18579     beforeDragDrop : function(target, e, id){
18580         this.cancelExpand();
18581         return true;
18582     },
18583     
18584     afterRepair : function(data){
18585         if(data && Roo.enableFx){
18586             data.node.ui.highlight();
18587         }
18588         this.hideProxy();
18589     } 
18590     
18591 });
18592
18593 }
18594 /*
18595  * Based on:
18596  * Ext JS Library 1.1.1
18597  * Copyright(c) 2006-2007, Ext JS, LLC.
18598  *
18599  * Originally Released Under LGPL - original licence link has changed is not relivant.
18600  *
18601  * Fork - LGPL
18602  * <script type="text/javascript">
18603  */
18604  
18605
18606 if(Roo.dd.DragZone){
18607 Roo.tree.TreeDragZone = function(tree, config){
18608     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18609     this.tree = tree;
18610 };
18611
18612 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18613     ddGroup : "TreeDD",
18614    
18615     onBeforeDrag : function(data, e){
18616         var n = data.node;
18617         return n && n.draggable && !n.disabled;
18618     },
18619      
18620     
18621     onInitDrag : function(e){
18622         var data = this.dragData;
18623         this.tree.getSelectionModel().select(data.node);
18624         this.proxy.update("");
18625         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18626         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18627     },
18628     
18629     getRepairXY : function(e, data){
18630         return data.node.ui.getDDRepairXY();
18631     },
18632     
18633     onEndDrag : function(data, e){
18634         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18635         
18636         
18637     },
18638     
18639     onValidDrop : function(dd, e, id){
18640         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18641         this.hideProxy();
18642     },
18643     
18644     beforeInvalidDrop : function(e, id){
18645         // this scrolls the original position back into view
18646         var sm = this.tree.getSelectionModel();
18647         sm.clearSelections();
18648         sm.select(this.dragData.node);
18649     }
18650 });
18651 }/*
18652  * Based on:
18653  * Ext JS Library 1.1.1
18654  * Copyright(c) 2006-2007, Ext JS, LLC.
18655  *
18656  * Originally Released Under LGPL - original licence link has changed is not relivant.
18657  *
18658  * Fork - LGPL
18659  * <script type="text/javascript">
18660  */
18661 /**
18662  * @class Roo.tree.TreeEditor
18663  * @extends Roo.Editor
18664  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18665  * as the editor field.
18666  * @constructor
18667  * @param {Object} config (used to be the tree panel.)
18668  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18669  * 
18670  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18671  * @cfg {Roo.form.TextField|Object} field The field configuration
18672  *
18673  * 
18674  */
18675 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18676     var tree = config;
18677     var field;
18678     if (oldconfig) { // old style..
18679         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18680     } else {
18681         // new style..
18682         tree = config.tree;
18683         config.field = config.field  || {};
18684         config.field.xtype = 'TextField';
18685         field = Roo.factory(config.field, Roo.form);
18686     }
18687     config = config || {};
18688     
18689     
18690     this.addEvents({
18691         /**
18692          * @event beforenodeedit
18693          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18694          * false from the handler of this event.
18695          * @param {Editor} this
18696          * @param {Roo.tree.Node} node 
18697          */
18698         "beforenodeedit" : true
18699     });
18700     
18701     //Roo.log(config);
18702     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18703
18704     this.tree = tree;
18705
18706     tree.on('beforeclick', this.beforeNodeClick, this);
18707     tree.getTreeEl().on('mousedown', this.hide, this);
18708     this.on('complete', this.updateNode, this);
18709     this.on('beforestartedit', this.fitToTree, this);
18710     this.on('startedit', this.bindScroll, this, {delay:10});
18711     this.on('specialkey', this.onSpecialKey, this);
18712 };
18713
18714 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18715     /**
18716      * @cfg {String} alignment
18717      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18718      */
18719     alignment: "l-l",
18720     // inherit
18721     autoSize: false,
18722     /**
18723      * @cfg {Boolean} hideEl
18724      * True to hide the bound element while the editor is displayed (defaults to false)
18725      */
18726     hideEl : false,
18727     /**
18728      * @cfg {String} cls
18729      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18730      */
18731     cls: "x-small-editor x-tree-editor",
18732     /**
18733      * @cfg {Boolean} shim
18734      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18735      */
18736     shim:false,
18737     // inherit
18738     shadow:"frame",
18739     /**
18740      * @cfg {Number} maxWidth
18741      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18742      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18743      * scroll and client offsets into account prior to each edit.
18744      */
18745     maxWidth: 250,
18746
18747     editDelay : 350,
18748
18749     // private
18750     fitToTree : function(ed, el){
18751         var td = this.tree.getTreeEl().dom, nd = el.dom;
18752         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18753             td.scrollLeft = nd.offsetLeft;
18754         }
18755         var w = Math.min(
18756                 this.maxWidth,
18757                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18758         this.setSize(w, '');
18759         
18760         return this.fireEvent('beforenodeedit', this, this.editNode);
18761         
18762     },
18763
18764     // private
18765     triggerEdit : function(node){
18766         this.completeEdit();
18767         this.editNode = node;
18768         this.startEdit(node.ui.textNode, node.text);
18769     },
18770
18771     // private
18772     bindScroll : function(){
18773         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18774     },
18775
18776     // private
18777     beforeNodeClick : function(node, e){
18778         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18779         this.lastClick = new Date();
18780         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18781             e.stopEvent();
18782             this.triggerEdit(node);
18783             return false;
18784         }
18785         return true;
18786     },
18787
18788     // private
18789     updateNode : function(ed, value){
18790         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18791         this.editNode.setText(value);
18792     },
18793
18794     // private
18795     onHide : function(){
18796         Roo.tree.TreeEditor.superclass.onHide.call(this);
18797         if(this.editNode){
18798             this.editNode.ui.focus();
18799         }
18800     },
18801
18802     // private
18803     onSpecialKey : function(field, e){
18804         var k = e.getKey();
18805         if(k == e.ESC){
18806             e.stopEvent();
18807             this.cancelEdit();
18808         }else if(k == e.ENTER && !e.hasModifier()){
18809             e.stopEvent();
18810             this.completeEdit();
18811         }
18812     }
18813 });//<Script type="text/javascript">
18814 /*
18815  * Based on:
18816  * Ext JS Library 1.1.1
18817  * Copyright(c) 2006-2007, Ext JS, LLC.
18818  *
18819  * Originally Released Under LGPL - original licence link has changed is not relivant.
18820  *
18821  * Fork - LGPL
18822  * <script type="text/javascript">
18823  */
18824  
18825 /**
18826  * Not documented??? - probably should be...
18827  */
18828
18829 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18830     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18831     
18832     renderElements : function(n, a, targetNode, bulkRender){
18833         //consel.log("renderElements?");
18834         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18835
18836         var t = n.getOwnerTree();
18837         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18838         
18839         var cols = t.columns;
18840         var bw = t.borderWidth;
18841         var c = cols[0];
18842         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18843          var cb = typeof a.checked == "boolean";
18844         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18845         var colcls = 'x-t-' + tid + '-c0';
18846         var buf = [
18847             '<li class="x-tree-node">',
18848             
18849                 
18850                 '<div class="x-tree-node-el ', a.cls,'">',
18851                     // extran...
18852                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18853                 
18854                 
18855                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18856                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18857                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18858                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18859                            (a.iconCls ? ' '+a.iconCls : ''),
18860                            '" unselectable="on" />',
18861                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18862                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18863                              
18864                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18865                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18866                             '<span unselectable="on" qtip="' + tx + '">',
18867                              tx,
18868                              '</span></a>' ,
18869                     '</div>',
18870                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18871                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18872                  ];
18873         for(var i = 1, len = cols.length; i < len; i++){
18874             c = cols[i];
18875             colcls = 'x-t-' + tid + '-c' +i;
18876             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18877             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18878                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18879                       "</div>");
18880          }
18881          
18882          buf.push(
18883             '</a>',
18884             '<div class="x-clear"></div></div>',
18885             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18886             "</li>");
18887         
18888         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18889             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18890                                 n.nextSibling.ui.getEl(), buf.join(""));
18891         }else{
18892             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18893         }
18894         var el = this.wrap.firstChild;
18895         this.elRow = el;
18896         this.elNode = el.firstChild;
18897         this.ranchor = el.childNodes[1];
18898         this.ctNode = this.wrap.childNodes[1];
18899         var cs = el.firstChild.childNodes;
18900         this.indentNode = cs[0];
18901         this.ecNode = cs[1];
18902         this.iconNode = cs[2];
18903         var index = 3;
18904         if(cb){
18905             this.checkbox = cs[3];
18906             index++;
18907         }
18908         this.anchor = cs[index];
18909         
18910         this.textNode = cs[index].firstChild;
18911         
18912         //el.on("click", this.onClick, this);
18913         //el.on("dblclick", this.onDblClick, this);
18914         
18915         
18916        // console.log(this);
18917     },
18918     initEvents : function(){
18919         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18920         
18921             
18922         var a = this.ranchor;
18923
18924         var el = Roo.get(a);
18925
18926         if(Roo.isOpera){ // opera render bug ignores the CSS
18927             el.setStyle("text-decoration", "none");
18928         }
18929
18930         el.on("click", this.onClick, this);
18931         el.on("dblclick", this.onDblClick, this);
18932         el.on("contextmenu", this.onContextMenu, this);
18933         
18934     },
18935     
18936     /*onSelectedChange : function(state){
18937         if(state){
18938             this.focus();
18939             this.addClass("x-tree-selected");
18940         }else{
18941             //this.blur();
18942             this.removeClass("x-tree-selected");
18943         }
18944     },*/
18945     addClass : function(cls){
18946         if(this.elRow){
18947             Roo.fly(this.elRow).addClass(cls);
18948         }
18949         
18950     },
18951     
18952     
18953     removeClass : function(cls){
18954         if(this.elRow){
18955             Roo.fly(this.elRow).removeClass(cls);
18956         }
18957     }
18958
18959     
18960     
18961 });//<Script type="text/javascript">
18962
18963 /*
18964  * Based on:
18965  * Ext JS Library 1.1.1
18966  * Copyright(c) 2006-2007, Ext JS, LLC.
18967  *
18968  * Originally Released Under LGPL - original licence link has changed is not relivant.
18969  *
18970  * Fork - LGPL
18971  * <script type="text/javascript">
18972  */
18973  
18974
18975 /**
18976  * @class Roo.tree.ColumnTree
18977  * @extends Roo.data.TreePanel
18978  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
18979  * @cfg {int} borderWidth  compined right/left border allowance
18980  * @constructor
18981  * @param {String/HTMLElement/Element} el The container element
18982  * @param {Object} config
18983  */
18984 Roo.tree.ColumnTree =  function(el, config)
18985 {
18986    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
18987    this.addEvents({
18988         /**
18989         * @event resize
18990         * Fire this event on a container when it resizes
18991         * @param {int} w Width
18992         * @param {int} h Height
18993         */
18994        "resize" : true
18995     });
18996     this.on('resize', this.onResize, this);
18997 };
18998
18999 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19000     //lines:false,
19001     
19002     
19003     borderWidth: Roo.isBorderBox ? 0 : 2, 
19004     headEls : false,
19005     
19006     render : function(){
19007         // add the header.....
19008        
19009         Roo.tree.ColumnTree.superclass.render.apply(this);
19010         
19011         this.el.addClass('x-column-tree');
19012         
19013         this.headers = this.el.createChild(
19014             {cls:'x-tree-headers'},this.innerCt.dom);
19015    
19016         var cols = this.columns, c;
19017         var totalWidth = 0;
19018         this.headEls = [];
19019         var  len = cols.length;
19020         for(var i = 0; i < len; i++){
19021              c = cols[i];
19022              totalWidth += c.width;
19023             this.headEls.push(this.headers.createChild({
19024                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19025                  cn: {
19026                      cls:'x-tree-hd-text',
19027                      html: c.header
19028                  },
19029                  style:'width:'+(c.width-this.borderWidth)+'px;'
19030              }));
19031         }
19032         this.headers.createChild({cls:'x-clear'});
19033         // prevent floats from wrapping when clipped
19034         this.headers.setWidth(totalWidth);
19035         //this.innerCt.setWidth(totalWidth);
19036         this.innerCt.setStyle({ overflow: 'auto' });
19037         this.onResize(this.width, this.height);
19038              
19039         
19040     },
19041     onResize : function(w,h)
19042     {
19043         this.height = h;
19044         this.width = w;
19045         // resize cols..
19046         this.innerCt.setWidth(this.width);
19047         this.innerCt.setHeight(this.height-20);
19048         
19049         // headers...
19050         var cols = this.columns, c;
19051         var totalWidth = 0;
19052         var expEl = false;
19053         var len = cols.length;
19054         for(var i = 0; i < len; i++){
19055             c = cols[i];
19056             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19057                 // it's the expander..
19058                 expEl  = this.headEls[i];
19059                 continue;
19060             }
19061             totalWidth += c.width;
19062             
19063         }
19064         if (expEl) {
19065             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19066         }
19067         this.headers.setWidth(w-20);
19068
19069         
19070         
19071         
19072     }
19073 });
19074 /*
19075  * Based on:
19076  * Ext JS Library 1.1.1
19077  * Copyright(c) 2006-2007, Ext JS, LLC.
19078  *
19079  * Originally Released Under LGPL - original licence link has changed is not relivant.
19080  *
19081  * Fork - LGPL
19082  * <script type="text/javascript">
19083  */
19084  
19085 /**
19086  * @class Roo.menu.Menu
19087  * @extends Roo.util.Observable
19088  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19089  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19090  * @constructor
19091  * Creates a new Menu
19092  * @param {Object} config Configuration options
19093  */
19094 Roo.menu.Menu = function(config){
19095     Roo.apply(this, config);
19096     this.id = this.id || Roo.id();
19097     this.addEvents({
19098         /**
19099          * @event beforeshow
19100          * Fires before this menu is displayed
19101          * @param {Roo.menu.Menu} this
19102          */
19103         beforeshow : true,
19104         /**
19105          * @event beforehide
19106          * Fires before this menu is hidden
19107          * @param {Roo.menu.Menu} this
19108          */
19109         beforehide : true,
19110         /**
19111          * @event show
19112          * Fires after this menu is displayed
19113          * @param {Roo.menu.Menu} this
19114          */
19115         show : true,
19116         /**
19117          * @event hide
19118          * Fires after this menu is hidden
19119          * @param {Roo.menu.Menu} this
19120          */
19121         hide : true,
19122         /**
19123          * @event click
19124          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19125          * @param {Roo.menu.Menu} this
19126          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19127          * @param {Roo.EventObject} e
19128          */
19129         click : true,
19130         /**
19131          * @event mouseover
19132          * Fires when the mouse is hovering over this menu
19133          * @param {Roo.menu.Menu} this
19134          * @param {Roo.EventObject} e
19135          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19136          */
19137         mouseover : true,
19138         /**
19139          * @event mouseout
19140          * Fires when the mouse exits this menu
19141          * @param {Roo.menu.Menu} this
19142          * @param {Roo.EventObject} e
19143          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19144          */
19145         mouseout : true,
19146         /**
19147          * @event itemclick
19148          * Fires when a menu item contained in this menu is clicked
19149          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19150          * @param {Roo.EventObject} e
19151          */
19152         itemclick: true
19153     });
19154     if (this.registerMenu) {
19155         Roo.menu.MenuMgr.register(this);
19156     }
19157     
19158     var mis = this.items;
19159     this.items = new Roo.util.MixedCollection();
19160     if(mis){
19161         this.add.apply(this, mis);
19162     }
19163 };
19164
19165 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19166     /**
19167      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19168      */
19169     minWidth : 120,
19170     /**
19171      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19172      * for bottom-right shadow (defaults to "sides")
19173      */
19174     shadow : "sides",
19175     /**
19176      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19177      * this menu (defaults to "tl-tr?")
19178      */
19179     subMenuAlign : "tl-tr?",
19180     /**
19181      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19182      * relative to its element of origin (defaults to "tl-bl?")
19183      */
19184     defaultAlign : "tl-bl?",
19185     /**
19186      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19187      */
19188     allowOtherMenus : false,
19189     /**
19190      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19191      */
19192     registerMenu : true,
19193
19194     hidden:true,
19195
19196     // private
19197     render : function(){
19198         if(this.el){
19199             return;
19200         }
19201         var el = this.el = new Roo.Layer({
19202             cls: "x-menu",
19203             shadow:this.shadow,
19204             constrain: false,
19205             parentEl: this.parentEl || document.body,
19206             zindex:15000
19207         });
19208
19209         this.keyNav = new Roo.menu.MenuNav(this);
19210
19211         if(this.plain){
19212             el.addClass("x-menu-plain");
19213         }
19214         if(this.cls){
19215             el.addClass(this.cls);
19216         }
19217         // generic focus element
19218         this.focusEl = el.createChild({
19219             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19220         });
19221         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19222         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19223         
19224         ul.on("mouseover", this.onMouseOver, this);
19225         ul.on("mouseout", this.onMouseOut, this);
19226         this.items.each(function(item){
19227             if (item.hidden) {
19228                 return;
19229             }
19230             
19231             var li = document.createElement("li");
19232             li.className = "x-menu-list-item";
19233             ul.dom.appendChild(li);
19234             item.render(li, this);
19235         }, this);
19236         this.ul = ul;
19237         this.autoWidth();
19238     },
19239
19240     // private
19241     autoWidth : function(){
19242         var el = this.el, ul = this.ul;
19243         if(!el){
19244             return;
19245         }
19246         var w = this.width;
19247         if(w){
19248             el.setWidth(w);
19249         }else if(Roo.isIE){
19250             el.setWidth(this.minWidth);
19251             var t = el.dom.offsetWidth; // force recalc
19252             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19253         }
19254     },
19255
19256     // private
19257     delayAutoWidth : function(){
19258         if(this.rendered){
19259             if(!this.awTask){
19260                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19261             }
19262             this.awTask.delay(20);
19263         }
19264     },
19265
19266     // private
19267     findTargetItem : function(e){
19268         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19269         if(t && t.menuItemId){
19270             return this.items.get(t.menuItemId);
19271         }
19272     },
19273
19274     // private
19275     onClick : function(e){
19276         Roo.log("menu.onClick");
19277         var t = this.findTargetItem(e);
19278         if(!t){
19279             return;
19280         }
19281         Roo.log(e);
19282         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19283             if(t == this.activeItem && t.shouldDeactivate(e)){
19284                 this.activeItem.deactivate();
19285                 delete this.activeItem;
19286                 return;
19287             }
19288             if(t.canActivate){
19289                 this.setActiveItem(t, true);
19290             }
19291             return;
19292             
19293             
19294         }
19295         
19296         t.onClick(e);
19297         this.fireEvent("click", this, t, e);
19298     },
19299
19300     // private
19301     setActiveItem : function(item, autoExpand){
19302         if(item != this.activeItem){
19303             if(this.activeItem){
19304                 this.activeItem.deactivate();
19305             }
19306             this.activeItem = item;
19307             item.activate(autoExpand);
19308         }else if(autoExpand){
19309             item.expandMenu();
19310         }
19311     },
19312
19313     // private
19314     tryActivate : function(start, step){
19315         var items = this.items;
19316         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19317             var item = items.get(i);
19318             if(!item.disabled && item.canActivate){
19319                 this.setActiveItem(item, false);
19320                 return item;
19321             }
19322         }
19323         return false;
19324     },
19325
19326     // private
19327     onMouseOver : function(e){
19328         var t;
19329         if(t = this.findTargetItem(e)){
19330             if(t.canActivate && !t.disabled){
19331                 this.setActiveItem(t, true);
19332             }
19333         }
19334         this.fireEvent("mouseover", this, e, t);
19335     },
19336
19337     // private
19338     onMouseOut : function(e){
19339         var t;
19340         if(t = this.findTargetItem(e)){
19341             if(t == this.activeItem && t.shouldDeactivate(e)){
19342                 this.activeItem.deactivate();
19343                 delete this.activeItem;
19344             }
19345         }
19346         this.fireEvent("mouseout", this, e, t);
19347     },
19348
19349     /**
19350      * Read-only.  Returns true if the menu is currently displayed, else false.
19351      * @type Boolean
19352      */
19353     isVisible : function(){
19354         return this.el && !this.hidden;
19355     },
19356
19357     /**
19358      * Displays this menu relative to another element
19359      * @param {String/HTMLElement/Roo.Element} element The element to align to
19360      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19361      * the element (defaults to this.defaultAlign)
19362      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19363      */
19364     show : function(el, pos, parentMenu){
19365         this.parentMenu = parentMenu;
19366         if(!this.el){
19367             this.render();
19368         }
19369         this.fireEvent("beforeshow", this);
19370         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19371     },
19372
19373     /**
19374      * Displays this menu at a specific xy position
19375      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19376      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19377      */
19378     showAt : function(xy, parentMenu, /* private: */_e){
19379         this.parentMenu = parentMenu;
19380         if(!this.el){
19381             this.render();
19382         }
19383         if(_e !== false){
19384             this.fireEvent("beforeshow", this);
19385             xy = this.el.adjustForConstraints(xy);
19386         }
19387         this.el.setXY(xy);
19388         this.el.show();
19389         this.hidden = false;
19390         this.focus();
19391         this.fireEvent("show", this);
19392     },
19393
19394     focus : function(){
19395         if(!this.hidden){
19396             this.doFocus.defer(50, this);
19397         }
19398     },
19399
19400     doFocus : function(){
19401         if(!this.hidden){
19402             this.focusEl.focus();
19403         }
19404     },
19405
19406     /**
19407      * Hides this menu and optionally all parent menus
19408      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19409      */
19410     hide : function(deep){
19411         if(this.el && this.isVisible()){
19412             this.fireEvent("beforehide", this);
19413             if(this.activeItem){
19414                 this.activeItem.deactivate();
19415                 this.activeItem = null;
19416             }
19417             this.el.hide();
19418             this.hidden = true;
19419             this.fireEvent("hide", this);
19420         }
19421         if(deep === true && this.parentMenu){
19422             this.parentMenu.hide(true);
19423         }
19424     },
19425
19426     /**
19427      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19428      * Any of the following are valid:
19429      * <ul>
19430      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19431      * <li>An HTMLElement object which will be converted to a menu item</li>
19432      * <li>A menu item config object that will be created as a new menu item</li>
19433      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19434      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19435      * </ul>
19436      * Usage:
19437      * <pre><code>
19438 // Create the menu
19439 var menu = new Roo.menu.Menu();
19440
19441 // Create a menu item to add by reference
19442 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19443
19444 // Add a bunch of items at once using different methods.
19445 // Only the last item added will be returned.
19446 var item = menu.add(
19447     menuItem,                // add existing item by ref
19448     'Dynamic Item',          // new TextItem
19449     '-',                     // new separator
19450     { text: 'Config Item' }  // new item by config
19451 );
19452 </code></pre>
19453      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19454      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19455      */
19456     add : function(){
19457         var a = arguments, l = a.length, item;
19458         for(var i = 0; i < l; i++){
19459             var el = a[i];
19460             if ((typeof(el) == "object") && el.xtype && el.xns) {
19461                 el = Roo.factory(el, Roo.menu);
19462             }
19463             
19464             if(el.render){ // some kind of Item
19465                 item = this.addItem(el);
19466             }else if(typeof el == "string"){ // string
19467                 if(el == "separator" || el == "-"){
19468                     item = this.addSeparator();
19469                 }else{
19470                     item = this.addText(el);
19471                 }
19472             }else if(el.tagName || el.el){ // element
19473                 item = this.addElement(el);
19474             }else if(typeof el == "object"){ // must be menu item config?
19475                 item = this.addMenuItem(el);
19476             }
19477         }
19478         return item;
19479     },
19480
19481     /**
19482      * Returns this menu's underlying {@link Roo.Element} object
19483      * @return {Roo.Element} The element
19484      */
19485     getEl : function(){
19486         if(!this.el){
19487             this.render();
19488         }
19489         return this.el;
19490     },
19491
19492     /**
19493      * Adds a separator bar to the menu
19494      * @return {Roo.menu.Item} The menu item that was added
19495      */
19496     addSeparator : function(){
19497         return this.addItem(new Roo.menu.Separator());
19498     },
19499
19500     /**
19501      * Adds an {@link Roo.Element} object to the menu
19502      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19503      * @return {Roo.menu.Item} The menu item that was added
19504      */
19505     addElement : function(el){
19506         return this.addItem(new Roo.menu.BaseItem(el));
19507     },
19508
19509     /**
19510      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19511      * @param {Roo.menu.Item} item The menu item to add
19512      * @return {Roo.menu.Item} The menu item that was added
19513      */
19514     addItem : function(item){
19515         this.items.add(item);
19516         if(this.ul){
19517             var li = document.createElement("li");
19518             li.className = "x-menu-list-item";
19519             this.ul.dom.appendChild(li);
19520             item.render(li, this);
19521             this.delayAutoWidth();
19522         }
19523         return item;
19524     },
19525
19526     /**
19527      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19528      * @param {Object} config A MenuItem config object
19529      * @return {Roo.menu.Item} The menu item that was added
19530      */
19531     addMenuItem : function(config){
19532         if(!(config instanceof Roo.menu.Item)){
19533             if(typeof config.checked == "boolean"){ // must be check menu item config?
19534                 config = new Roo.menu.CheckItem(config);
19535             }else{
19536                 config = new Roo.menu.Item(config);
19537             }
19538         }
19539         return this.addItem(config);
19540     },
19541
19542     /**
19543      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19544      * @param {String} text The text to display in the menu item
19545      * @return {Roo.menu.Item} The menu item that was added
19546      */
19547     addText : function(text){
19548         return this.addItem(new Roo.menu.TextItem({ text : text }));
19549     },
19550
19551     /**
19552      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19553      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19554      * @param {Roo.menu.Item} item The menu item to add
19555      * @return {Roo.menu.Item} The menu item that was added
19556      */
19557     insert : function(index, item){
19558         this.items.insert(index, item);
19559         if(this.ul){
19560             var li = document.createElement("li");
19561             li.className = "x-menu-list-item";
19562             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19563             item.render(li, this);
19564             this.delayAutoWidth();
19565         }
19566         return item;
19567     },
19568
19569     /**
19570      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19571      * @param {Roo.menu.Item} item The menu item to remove
19572      */
19573     remove : function(item){
19574         this.items.removeKey(item.id);
19575         item.destroy();
19576     },
19577
19578     /**
19579      * Removes and destroys all items in the menu
19580      */
19581     removeAll : function(){
19582         var f;
19583         while(f = this.items.first()){
19584             this.remove(f);
19585         }
19586     }
19587 });
19588
19589 // MenuNav is a private utility class used internally by the Menu
19590 Roo.menu.MenuNav = function(menu){
19591     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19592     this.scope = this.menu = menu;
19593 };
19594
19595 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19596     doRelay : function(e, h){
19597         var k = e.getKey();
19598         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19599             this.menu.tryActivate(0, 1);
19600             return false;
19601         }
19602         return h.call(this.scope || this, e, this.menu);
19603     },
19604
19605     up : function(e, m){
19606         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19607             m.tryActivate(m.items.length-1, -1);
19608         }
19609     },
19610
19611     down : function(e, m){
19612         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19613             m.tryActivate(0, 1);
19614         }
19615     },
19616
19617     right : function(e, m){
19618         if(m.activeItem){
19619             m.activeItem.expandMenu(true);
19620         }
19621     },
19622
19623     left : function(e, m){
19624         m.hide();
19625         if(m.parentMenu && m.parentMenu.activeItem){
19626             m.parentMenu.activeItem.activate();
19627         }
19628     },
19629
19630     enter : function(e, m){
19631         if(m.activeItem){
19632             e.stopPropagation();
19633             m.activeItem.onClick(e);
19634             m.fireEvent("click", this, m.activeItem);
19635             return true;
19636         }
19637     }
19638 });/*
19639  * Based on:
19640  * Ext JS Library 1.1.1
19641  * Copyright(c) 2006-2007, Ext JS, LLC.
19642  *
19643  * Originally Released Under LGPL - original licence link has changed is not relivant.
19644  *
19645  * Fork - LGPL
19646  * <script type="text/javascript">
19647  */
19648  
19649 /**
19650  * @class Roo.menu.MenuMgr
19651  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19652  * @singleton
19653  */
19654 Roo.menu.MenuMgr = function(){
19655    var menus, active, groups = {}, attached = false, lastShow = new Date();
19656
19657    // private - called when first menu is created
19658    function init(){
19659        menus = {};
19660        active = new Roo.util.MixedCollection();
19661        Roo.get(document).addKeyListener(27, function(){
19662            if(active.length > 0){
19663                hideAll();
19664            }
19665        });
19666    }
19667
19668    // private
19669    function hideAll(){
19670        if(active && active.length > 0){
19671            var c = active.clone();
19672            c.each(function(m){
19673                m.hide();
19674            });
19675        }
19676    }
19677
19678    // private
19679    function onHide(m){
19680        active.remove(m);
19681        if(active.length < 1){
19682            Roo.get(document).un("mousedown", onMouseDown);
19683            attached = false;
19684        }
19685    }
19686
19687    // private
19688    function onShow(m){
19689        var last = active.last();
19690        lastShow = new Date();
19691        active.add(m);
19692        if(!attached){
19693            Roo.get(document).on("mousedown", onMouseDown);
19694            attached = true;
19695        }
19696        if(m.parentMenu){
19697           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19698           m.parentMenu.activeChild = m;
19699        }else if(last && last.isVisible()){
19700           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19701        }
19702    }
19703
19704    // private
19705    function onBeforeHide(m){
19706        if(m.activeChild){
19707            m.activeChild.hide();
19708        }
19709        if(m.autoHideTimer){
19710            clearTimeout(m.autoHideTimer);
19711            delete m.autoHideTimer;
19712        }
19713    }
19714
19715    // private
19716    function onBeforeShow(m){
19717        var pm = m.parentMenu;
19718        if(!pm && !m.allowOtherMenus){
19719            hideAll();
19720        }else if(pm && pm.activeChild && active != m){
19721            pm.activeChild.hide();
19722        }
19723    }
19724
19725    // private
19726    function onMouseDown(e){
19727        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19728            hideAll();
19729        }
19730    }
19731
19732    // private
19733    function onBeforeCheck(mi, state){
19734        if(state){
19735            var g = groups[mi.group];
19736            for(var i = 0, l = g.length; i < l; i++){
19737                if(g[i] != mi){
19738                    g[i].setChecked(false);
19739                }
19740            }
19741        }
19742    }
19743
19744    return {
19745
19746        /**
19747         * Hides all menus that are currently visible
19748         */
19749        hideAll : function(){
19750             hideAll();  
19751        },
19752
19753        // private
19754        register : function(menu){
19755            if(!menus){
19756                init();
19757            }
19758            menus[menu.id] = menu;
19759            menu.on("beforehide", onBeforeHide);
19760            menu.on("hide", onHide);
19761            menu.on("beforeshow", onBeforeShow);
19762            menu.on("show", onShow);
19763            var g = menu.group;
19764            if(g && menu.events["checkchange"]){
19765                if(!groups[g]){
19766                    groups[g] = [];
19767                }
19768                groups[g].push(menu);
19769                menu.on("checkchange", onCheck);
19770            }
19771        },
19772
19773         /**
19774          * Returns a {@link Roo.menu.Menu} object
19775          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19776          * be used to generate and return a new Menu instance.
19777          */
19778        get : function(menu){
19779            if(typeof menu == "string"){ // menu id
19780                return menus[menu];
19781            }else if(menu.events){  // menu instance
19782                return menu;
19783            }else if(typeof menu.length == 'number'){ // array of menu items?
19784                return new Roo.menu.Menu({items:menu});
19785            }else{ // otherwise, must be a config
19786                return new Roo.menu.Menu(menu);
19787            }
19788        },
19789
19790        // private
19791        unregister : function(menu){
19792            delete menus[menu.id];
19793            menu.un("beforehide", onBeforeHide);
19794            menu.un("hide", onHide);
19795            menu.un("beforeshow", onBeforeShow);
19796            menu.un("show", onShow);
19797            var g = menu.group;
19798            if(g && menu.events["checkchange"]){
19799                groups[g].remove(menu);
19800                menu.un("checkchange", onCheck);
19801            }
19802        },
19803
19804        // private
19805        registerCheckable : function(menuItem){
19806            var g = menuItem.group;
19807            if(g){
19808                if(!groups[g]){
19809                    groups[g] = [];
19810                }
19811                groups[g].push(menuItem);
19812                menuItem.on("beforecheckchange", onBeforeCheck);
19813            }
19814        },
19815
19816        // private
19817        unregisterCheckable : function(menuItem){
19818            var g = menuItem.group;
19819            if(g){
19820                groups[g].remove(menuItem);
19821                menuItem.un("beforecheckchange", onBeforeCheck);
19822            }
19823        }
19824    };
19825 }();/*
19826  * Based on:
19827  * Ext JS Library 1.1.1
19828  * Copyright(c) 2006-2007, Ext JS, LLC.
19829  *
19830  * Originally Released Under LGPL - original licence link has changed is not relivant.
19831  *
19832  * Fork - LGPL
19833  * <script type="text/javascript">
19834  */
19835  
19836
19837 /**
19838  * @class Roo.menu.BaseItem
19839  * @extends Roo.Component
19840  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19841  * management and base configuration options shared by all menu components.
19842  * @constructor
19843  * Creates a new BaseItem
19844  * @param {Object} config Configuration options
19845  */
19846 Roo.menu.BaseItem = function(config){
19847     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19848
19849     this.addEvents({
19850         /**
19851          * @event click
19852          * Fires when this item is clicked
19853          * @param {Roo.menu.BaseItem} this
19854          * @param {Roo.EventObject} e
19855          */
19856         click: true,
19857         /**
19858          * @event activate
19859          * Fires when this item is activated
19860          * @param {Roo.menu.BaseItem} this
19861          */
19862         activate : true,
19863         /**
19864          * @event deactivate
19865          * Fires when this item is deactivated
19866          * @param {Roo.menu.BaseItem} this
19867          */
19868         deactivate : true
19869     });
19870
19871     if(this.handler){
19872         this.on("click", this.handler, this.scope, true);
19873     }
19874 };
19875
19876 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19877     /**
19878      * @cfg {Function} handler
19879      * A function that will handle the click event of this menu item (defaults to undefined)
19880      */
19881     /**
19882      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19883      */
19884     canActivate : false,
19885     
19886      /**
19887      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19888      */
19889     hidden: false,
19890     
19891     /**
19892      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19893      */
19894     activeClass : "x-menu-item-active",
19895     /**
19896      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19897      */
19898     hideOnClick : true,
19899     /**
19900      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19901      */
19902     hideDelay : 100,
19903
19904     // private
19905     ctype: "Roo.menu.BaseItem",
19906
19907     // private
19908     actionMode : "container",
19909
19910     // private
19911     render : function(container, parentMenu){
19912         this.parentMenu = parentMenu;
19913         Roo.menu.BaseItem.superclass.render.call(this, container);
19914         this.container.menuItemId = this.id;
19915     },
19916
19917     // private
19918     onRender : function(container, position){
19919         this.el = Roo.get(this.el);
19920         container.dom.appendChild(this.el.dom);
19921     },
19922
19923     // private
19924     onClick : function(e){
19925         if(!this.disabled && this.fireEvent("click", this, e) !== false
19926                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19927             this.handleClick(e);
19928         }else{
19929             e.stopEvent();
19930         }
19931     },
19932
19933     // private
19934     activate : function(){
19935         if(this.disabled){
19936             return false;
19937         }
19938         var li = this.container;
19939         li.addClass(this.activeClass);
19940         this.region = li.getRegion().adjust(2, 2, -2, -2);
19941         this.fireEvent("activate", this);
19942         return true;
19943     },
19944
19945     // private
19946     deactivate : function(){
19947         this.container.removeClass(this.activeClass);
19948         this.fireEvent("deactivate", this);
19949     },
19950
19951     // private
19952     shouldDeactivate : function(e){
19953         return !this.region || !this.region.contains(e.getPoint());
19954     },
19955
19956     // private
19957     handleClick : function(e){
19958         if(this.hideOnClick){
19959             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19960         }
19961     },
19962
19963     // private
19964     expandMenu : function(autoActivate){
19965         // do nothing
19966     },
19967
19968     // private
19969     hideMenu : function(){
19970         // do nothing
19971     }
19972 });/*
19973  * Based on:
19974  * Ext JS Library 1.1.1
19975  * Copyright(c) 2006-2007, Ext JS, LLC.
19976  *
19977  * Originally Released Under LGPL - original licence link has changed is not relivant.
19978  *
19979  * Fork - LGPL
19980  * <script type="text/javascript">
19981  */
19982  
19983 /**
19984  * @class Roo.menu.Adapter
19985  * @extends Roo.menu.BaseItem
19986  * 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.
19987  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
19988  * @constructor
19989  * Creates a new Adapter
19990  * @param {Object} config Configuration options
19991  */
19992 Roo.menu.Adapter = function(component, config){
19993     Roo.menu.Adapter.superclass.constructor.call(this, config);
19994     this.component = component;
19995 };
19996 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
19997     // private
19998     canActivate : true,
19999
20000     // private
20001     onRender : function(container, position){
20002         this.component.render(container);
20003         this.el = this.component.getEl();
20004     },
20005
20006     // private
20007     activate : function(){
20008         if(this.disabled){
20009             return false;
20010         }
20011         this.component.focus();
20012         this.fireEvent("activate", this);
20013         return true;
20014     },
20015
20016     // private
20017     deactivate : function(){
20018         this.fireEvent("deactivate", this);
20019     },
20020
20021     // private
20022     disable : function(){
20023         this.component.disable();
20024         Roo.menu.Adapter.superclass.disable.call(this);
20025     },
20026
20027     // private
20028     enable : function(){
20029         this.component.enable();
20030         Roo.menu.Adapter.superclass.enable.call(this);
20031     }
20032 });/*
20033  * Based on:
20034  * Ext JS Library 1.1.1
20035  * Copyright(c) 2006-2007, Ext JS, LLC.
20036  *
20037  * Originally Released Under LGPL - original licence link has changed is not relivant.
20038  *
20039  * Fork - LGPL
20040  * <script type="text/javascript">
20041  */
20042
20043 /**
20044  * @class Roo.menu.TextItem
20045  * @extends Roo.menu.BaseItem
20046  * Adds a static text string to a menu, usually used as either a heading or group separator.
20047  * Note: old style constructor with text is still supported.
20048  * 
20049  * @constructor
20050  * Creates a new TextItem
20051  * @param {Object} cfg Configuration
20052  */
20053 Roo.menu.TextItem = function(cfg){
20054     if (typeof(cfg) == 'string') {
20055         this.text = cfg;
20056     } else {
20057         Roo.apply(this,cfg);
20058     }
20059     
20060     Roo.menu.TextItem.superclass.constructor.call(this);
20061 };
20062
20063 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20064     /**
20065      * @cfg {Boolean} text Text to show on item.
20066      */
20067     text : '',
20068     
20069     /**
20070      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20071      */
20072     hideOnClick : false,
20073     /**
20074      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20075      */
20076     itemCls : "x-menu-text",
20077
20078     // private
20079     onRender : function(){
20080         var s = document.createElement("span");
20081         s.className = this.itemCls;
20082         s.innerHTML = this.text;
20083         this.el = s;
20084         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20085     }
20086 });/*
20087  * Based on:
20088  * Ext JS Library 1.1.1
20089  * Copyright(c) 2006-2007, Ext JS, LLC.
20090  *
20091  * Originally Released Under LGPL - original licence link has changed is not relivant.
20092  *
20093  * Fork - LGPL
20094  * <script type="text/javascript">
20095  */
20096
20097 /**
20098  * @class Roo.menu.Separator
20099  * @extends Roo.menu.BaseItem
20100  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20101  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20102  * @constructor
20103  * @param {Object} config Configuration options
20104  */
20105 Roo.menu.Separator = function(config){
20106     Roo.menu.Separator.superclass.constructor.call(this, config);
20107 };
20108
20109 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20110     /**
20111      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20112      */
20113     itemCls : "x-menu-sep",
20114     /**
20115      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20116      */
20117     hideOnClick : false,
20118
20119     // private
20120     onRender : function(li){
20121         var s = document.createElement("span");
20122         s.className = this.itemCls;
20123         s.innerHTML = "&#160;";
20124         this.el = s;
20125         li.addClass("x-menu-sep-li");
20126         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20127     }
20128 });/*
20129  * Based on:
20130  * Ext JS Library 1.1.1
20131  * Copyright(c) 2006-2007, Ext JS, LLC.
20132  *
20133  * Originally Released Under LGPL - original licence link has changed is not relivant.
20134  *
20135  * Fork - LGPL
20136  * <script type="text/javascript">
20137  */
20138 /**
20139  * @class Roo.menu.Item
20140  * @extends Roo.menu.BaseItem
20141  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20142  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20143  * activation and click handling.
20144  * @constructor
20145  * Creates a new Item
20146  * @param {Object} config Configuration options
20147  */
20148 Roo.menu.Item = function(config){
20149     Roo.menu.Item.superclass.constructor.call(this, config);
20150     if(this.menu){
20151         this.menu = Roo.menu.MenuMgr.get(this.menu);
20152     }
20153 };
20154 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20155     
20156     /**
20157      * @cfg {String} text
20158      * The text to show on the menu item.
20159      */
20160     text: '',
20161      /**
20162      * @cfg {String} HTML to render in menu
20163      * The text to show on the menu item (HTML version).
20164      */
20165     html: '',
20166     /**
20167      * @cfg {String} icon
20168      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20169      */
20170     icon: undefined,
20171     /**
20172      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20173      */
20174     itemCls : "x-menu-item",
20175     /**
20176      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20177      */
20178     canActivate : true,
20179     /**
20180      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20181      */
20182     showDelay: 200,
20183     // doc'd in BaseItem
20184     hideDelay: 200,
20185
20186     // private
20187     ctype: "Roo.menu.Item",
20188     
20189     // private
20190     onRender : function(container, position){
20191         var el = document.createElement("a");
20192         el.hideFocus = true;
20193         el.unselectable = "on";
20194         el.href = this.href || "#";
20195         if(this.hrefTarget){
20196             el.target = this.hrefTarget;
20197         }
20198         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20199         
20200         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20201         
20202         el.innerHTML = String.format(
20203                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20204                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20205         this.el = el;
20206         Roo.menu.Item.superclass.onRender.call(this, container, position);
20207     },
20208
20209     /**
20210      * Sets the text to display in this menu item
20211      * @param {String} text The text to display
20212      * @param {Boolean} isHTML true to indicate text is pure html.
20213      */
20214     setText : function(text, isHTML){
20215         if (isHTML) {
20216             this.html = text;
20217         } else {
20218             this.text = text;
20219             this.html = '';
20220         }
20221         if(this.rendered){
20222             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20223      
20224             this.el.update(String.format(
20225                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20226                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20227             this.parentMenu.autoWidth();
20228         }
20229     },
20230
20231     // private
20232     handleClick : function(e){
20233         if(!this.href){ // if no link defined, stop the event automatically
20234             e.stopEvent();
20235         }
20236         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20237     },
20238
20239     // private
20240     activate : function(autoExpand){
20241         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20242             this.focus();
20243             if(autoExpand){
20244                 this.expandMenu();
20245             }
20246         }
20247         return true;
20248     },
20249
20250     // private
20251     shouldDeactivate : function(e){
20252         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20253             if(this.menu && this.menu.isVisible()){
20254                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20255             }
20256             return true;
20257         }
20258         return false;
20259     },
20260
20261     // private
20262     deactivate : function(){
20263         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20264         this.hideMenu();
20265     },
20266
20267     // private
20268     expandMenu : function(autoActivate){
20269         if(!this.disabled && this.menu){
20270             clearTimeout(this.hideTimer);
20271             delete this.hideTimer;
20272             if(!this.menu.isVisible() && !this.showTimer){
20273                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20274             }else if (this.menu.isVisible() && autoActivate){
20275                 this.menu.tryActivate(0, 1);
20276             }
20277         }
20278     },
20279
20280     // private
20281     deferExpand : function(autoActivate){
20282         delete this.showTimer;
20283         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20284         if(autoActivate){
20285             this.menu.tryActivate(0, 1);
20286         }
20287     },
20288
20289     // private
20290     hideMenu : function(){
20291         clearTimeout(this.showTimer);
20292         delete this.showTimer;
20293         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20294             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20295         }
20296     },
20297
20298     // private
20299     deferHide : function(){
20300         delete this.hideTimer;
20301         this.menu.hide();
20302     }
20303 });/*
20304  * Based on:
20305  * Ext JS Library 1.1.1
20306  * Copyright(c) 2006-2007, Ext JS, LLC.
20307  *
20308  * Originally Released Under LGPL - original licence link has changed is not relivant.
20309  *
20310  * Fork - LGPL
20311  * <script type="text/javascript">
20312  */
20313  
20314 /**
20315  * @class Roo.menu.CheckItem
20316  * @extends Roo.menu.Item
20317  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20318  * @constructor
20319  * Creates a new CheckItem
20320  * @param {Object} config Configuration options
20321  */
20322 Roo.menu.CheckItem = function(config){
20323     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20324     this.addEvents({
20325         /**
20326          * @event beforecheckchange
20327          * Fires before the checked value is set, providing an opportunity to cancel if needed
20328          * @param {Roo.menu.CheckItem} this
20329          * @param {Boolean} checked The new checked value that will be set
20330          */
20331         "beforecheckchange" : true,
20332         /**
20333          * @event checkchange
20334          * Fires after the checked value has been set
20335          * @param {Roo.menu.CheckItem} this
20336          * @param {Boolean} checked The checked value that was set
20337          */
20338         "checkchange" : true
20339     });
20340     if(this.checkHandler){
20341         this.on('checkchange', this.checkHandler, this.scope);
20342     }
20343 };
20344 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20345     /**
20346      * @cfg {String} group
20347      * All check items with the same group name will automatically be grouped into a single-select
20348      * radio button group (defaults to '')
20349      */
20350     /**
20351      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20352      */
20353     itemCls : "x-menu-item x-menu-check-item",
20354     /**
20355      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20356      */
20357     groupClass : "x-menu-group-item",
20358
20359     /**
20360      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20361      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20362      * initialized with checked = true will be rendered as checked.
20363      */
20364     checked: false,
20365
20366     // private
20367     ctype: "Roo.menu.CheckItem",
20368
20369     // private
20370     onRender : function(c){
20371         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20372         if(this.group){
20373             this.el.addClass(this.groupClass);
20374         }
20375         Roo.menu.MenuMgr.registerCheckable(this);
20376         if(this.checked){
20377             this.checked = false;
20378             this.setChecked(true, true);
20379         }
20380     },
20381
20382     // private
20383     destroy : function(){
20384         if(this.rendered){
20385             Roo.menu.MenuMgr.unregisterCheckable(this);
20386         }
20387         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20388     },
20389
20390     /**
20391      * Set the checked state of this item
20392      * @param {Boolean} checked The new checked value
20393      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20394      */
20395     setChecked : function(state, suppressEvent){
20396         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20397             if(this.container){
20398                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20399             }
20400             this.checked = state;
20401             if(suppressEvent !== true){
20402                 this.fireEvent("checkchange", this, state);
20403             }
20404         }
20405     },
20406
20407     // private
20408     handleClick : function(e){
20409        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20410            this.setChecked(!this.checked);
20411        }
20412        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20413     }
20414 });/*
20415  * Based on:
20416  * Ext JS Library 1.1.1
20417  * Copyright(c) 2006-2007, Ext JS, LLC.
20418  *
20419  * Originally Released Under LGPL - original licence link has changed is not relivant.
20420  *
20421  * Fork - LGPL
20422  * <script type="text/javascript">
20423  */
20424  
20425 /**
20426  * @class Roo.menu.DateItem
20427  * @extends Roo.menu.Adapter
20428  * A menu item that wraps the {@link Roo.DatPicker} component.
20429  * @constructor
20430  * Creates a new DateItem
20431  * @param {Object} config Configuration options
20432  */
20433 Roo.menu.DateItem = function(config){
20434     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20435     /** The Roo.DatePicker object @type Roo.DatePicker */
20436     this.picker = this.component;
20437     this.addEvents({select: true});
20438     
20439     this.picker.on("render", function(picker){
20440         picker.getEl().swallowEvent("click");
20441         picker.container.addClass("x-menu-date-item");
20442     });
20443
20444     this.picker.on("select", this.onSelect, this);
20445 };
20446
20447 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20448     // private
20449     onSelect : function(picker, date){
20450         this.fireEvent("select", this, date, picker);
20451         Roo.menu.DateItem.superclass.handleClick.call(this);
20452     }
20453 });/*
20454  * Based on:
20455  * Ext JS Library 1.1.1
20456  * Copyright(c) 2006-2007, Ext JS, LLC.
20457  *
20458  * Originally Released Under LGPL - original licence link has changed is not relivant.
20459  *
20460  * Fork - LGPL
20461  * <script type="text/javascript">
20462  */
20463  
20464 /**
20465  * @class Roo.menu.ColorItem
20466  * @extends Roo.menu.Adapter
20467  * A menu item that wraps the {@link Roo.ColorPalette} component.
20468  * @constructor
20469  * Creates a new ColorItem
20470  * @param {Object} config Configuration options
20471  */
20472 Roo.menu.ColorItem = function(config){
20473     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20474     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20475     this.palette = this.component;
20476     this.relayEvents(this.palette, ["select"]);
20477     if(this.selectHandler){
20478         this.on('select', this.selectHandler, this.scope);
20479     }
20480 };
20481 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20482  * Based on:
20483  * Ext JS Library 1.1.1
20484  * Copyright(c) 2006-2007, Ext JS, LLC.
20485  *
20486  * Originally Released Under LGPL - original licence link has changed is not relivant.
20487  *
20488  * Fork - LGPL
20489  * <script type="text/javascript">
20490  */
20491  
20492
20493 /**
20494  * @class Roo.menu.DateMenu
20495  * @extends Roo.menu.Menu
20496  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20497  * @constructor
20498  * Creates a new DateMenu
20499  * @param {Object} config Configuration options
20500  */
20501 Roo.menu.DateMenu = function(config){
20502     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20503     this.plain = true;
20504     var di = new Roo.menu.DateItem(config);
20505     this.add(di);
20506     /**
20507      * The {@link Roo.DatePicker} instance for this DateMenu
20508      * @type DatePicker
20509      */
20510     this.picker = di.picker;
20511     /**
20512      * @event select
20513      * @param {DatePicker} picker
20514      * @param {Date} date
20515      */
20516     this.relayEvents(di, ["select"]);
20517     this.on('beforeshow', function(){
20518         if(this.picker){
20519             this.picker.hideMonthPicker(false);
20520         }
20521     }, this);
20522 };
20523 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20524     cls:'x-date-menu'
20525 });/*
20526  * Based on:
20527  * Ext JS Library 1.1.1
20528  * Copyright(c) 2006-2007, Ext JS, LLC.
20529  *
20530  * Originally Released Under LGPL - original licence link has changed is not relivant.
20531  *
20532  * Fork - LGPL
20533  * <script type="text/javascript">
20534  */
20535  
20536
20537 /**
20538  * @class Roo.menu.ColorMenu
20539  * @extends Roo.menu.Menu
20540  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20541  * @constructor
20542  * Creates a new ColorMenu
20543  * @param {Object} config Configuration options
20544  */
20545 Roo.menu.ColorMenu = function(config){
20546     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20547     this.plain = true;
20548     var ci = new Roo.menu.ColorItem(config);
20549     this.add(ci);
20550     /**
20551      * The {@link Roo.ColorPalette} instance for this ColorMenu
20552      * @type ColorPalette
20553      */
20554     this.palette = ci.palette;
20555     /**
20556      * @event select
20557      * @param {ColorPalette} palette
20558      * @param {String} color
20559      */
20560     this.relayEvents(ci, ["select"]);
20561 };
20562 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20563  * Based on:
20564  * Ext JS Library 1.1.1
20565  * Copyright(c) 2006-2007, Ext JS, LLC.
20566  *
20567  * Originally Released Under LGPL - original licence link has changed is not relivant.
20568  *
20569  * Fork - LGPL
20570  * <script type="text/javascript">
20571  */
20572  
20573 /**
20574  * @class Roo.form.Field
20575  * @extends Roo.BoxComponent
20576  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20577  * @constructor
20578  * Creates a new Field
20579  * @param {Object} config Configuration options
20580  */
20581 Roo.form.Field = function(config){
20582     Roo.form.Field.superclass.constructor.call(this, config);
20583 };
20584
20585 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20586     /**
20587      * @cfg {String} fieldLabel Label to use when rendering a form.
20588      */
20589        /**
20590      * @cfg {String} qtip Mouse over tip
20591      */
20592      
20593     /**
20594      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20595      */
20596     invalidClass : "x-form-invalid",
20597     /**
20598      * @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")
20599      */
20600     invalidText : "The value in this field is invalid",
20601     /**
20602      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20603      */
20604     focusClass : "x-form-focus",
20605     /**
20606      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20607       automatic validation (defaults to "keyup").
20608      */
20609     validationEvent : "keyup",
20610     /**
20611      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20612      */
20613     validateOnBlur : true,
20614     /**
20615      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20616      */
20617     validationDelay : 250,
20618     /**
20619      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20620      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20621      */
20622     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20623     /**
20624      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20625      */
20626     fieldClass : "x-form-field",
20627     /**
20628      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20629      *<pre>
20630 Value         Description
20631 -----------   ----------------------------------------------------------------------
20632 qtip          Display a quick tip when the user hovers over the field
20633 title         Display a default browser title attribute popup
20634 under         Add a block div beneath the field containing the error text
20635 side          Add an error icon to the right of the field with a popup on hover
20636 [element id]  Add the error text directly to the innerHTML of the specified element
20637 </pre>
20638      */
20639     msgTarget : 'qtip',
20640     /**
20641      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20642      */
20643     msgFx : 'normal',
20644
20645     /**
20646      * @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.
20647      */
20648     readOnly : false,
20649
20650     /**
20651      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20652      */
20653     disabled : false,
20654
20655     /**
20656      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20657      */
20658     inputType : undefined,
20659     
20660     /**
20661      * @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).
20662          */
20663         tabIndex : undefined,
20664         
20665     // private
20666     isFormField : true,
20667
20668     // private
20669     hasFocus : false,
20670     /**
20671      * @property {Roo.Element} fieldEl
20672      * Element Containing the rendered Field (with label etc.)
20673      */
20674     /**
20675      * @cfg {Mixed} value A value to initialize this field with.
20676      */
20677     value : undefined,
20678
20679     /**
20680      * @cfg {String} name The field's HTML name attribute.
20681      */
20682     /**
20683      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20684      */
20685
20686         // private ??
20687         initComponent : function(){
20688         Roo.form.Field.superclass.initComponent.call(this);
20689         this.addEvents({
20690             /**
20691              * @event focus
20692              * Fires when this field receives input focus.
20693              * @param {Roo.form.Field} this
20694              */
20695             focus : true,
20696             /**
20697              * @event blur
20698              * Fires when this field loses input focus.
20699              * @param {Roo.form.Field} this
20700              */
20701             blur : true,
20702             /**
20703              * @event specialkey
20704              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20705              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20706              * @param {Roo.form.Field} this
20707              * @param {Roo.EventObject} e The event object
20708              */
20709             specialkey : true,
20710             /**
20711              * @event change
20712              * Fires just before the field blurs if the field value has changed.
20713              * @param {Roo.form.Field} this
20714              * @param {Mixed} newValue The new value
20715              * @param {Mixed} oldValue The original value
20716              */
20717             change : true,
20718             /**
20719              * @event invalid
20720              * Fires after the field has been marked as invalid.
20721              * @param {Roo.form.Field} this
20722              * @param {String} msg The validation message
20723              */
20724             invalid : true,
20725             /**
20726              * @event valid
20727              * Fires after the field has been validated with no errors.
20728              * @param {Roo.form.Field} this
20729              */
20730             valid : true,
20731              /**
20732              * @event keyup
20733              * Fires after the key up
20734              * @param {Roo.form.Field} this
20735              * @param {Roo.EventObject}  e The event Object
20736              */
20737             keyup : true
20738         });
20739     },
20740
20741     /**
20742      * Returns the name attribute of the field if available
20743      * @return {String} name The field name
20744      */
20745     getName: function(){
20746          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20747     },
20748
20749     // private
20750     onRender : function(ct, position){
20751         Roo.form.Field.superclass.onRender.call(this, ct, position);
20752         if(!this.el){
20753             var cfg = this.getAutoCreate();
20754             if(!cfg.name){
20755                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20756             }
20757             if (!cfg.name.length) {
20758                 delete cfg.name;
20759             }
20760             if(this.inputType){
20761                 cfg.type = this.inputType;
20762             }
20763             this.el = ct.createChild(cfg, position);
20764         }
20765         var type = this.el.dom.type;
20766         if(type){
20767             if(type == 'password'){
20768                 type = 'text';
20769             }
20770             this.el.addClass('x-form-'+type);
20771         }
20772         if(this.readOnly){
20773             this.el.dom.readOnly = true;
20774         }
20775         if(this.tabIndex !== undefined){
20776             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20777         }
20778
20779         this.el.addClass([this.fieldClass, this.cls]);
20780         this.initValue();
20781     },
20782
20783     /**
20784      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20785      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20786      * @return {Roo.form.Field} this
20787      */
20788     applyTo : function(target){
20789         this.allowDomMove = false;
20790         this.el = Roo.get(target);
20791         this.render(this.el.dom.parentNode);
20792         return this;
20793     },
20794
20795     // private
20796     initValue : function(){
20797         if(this.value !== undefined){
20798             this.setValue(this.value);
20799         }else if(this.el.dom.value.length > 0){
20800             this.setValue(this.el.dom.value);
20801         }
20802     },
20803
20804     /**
20805      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20806      */
20807     isDirty : function() {
20808         if(this.disabled) {
20809             return false;
20810         }
20811         return String(this.getValue()) !== String(this.originalValue);
20812     },
20813
20814     // private
20815     afterRender : function(){
20816         Roo.form.Field.superclass.afterRender.call(this);
20817         this.initEvents();
20818     },
20819
20820     // private
20821     fireKey : function(e){
20822         //Roo.log('field ' + e.getKey());
20823         if(e.isNavKeyPress()){
20824             this.fireEvent("specialkey", this, e);
20825         }
20826     },
20827
20828     /**
20829      * Resets the current field value to the originally loaded value and clears any validation messages
20830      */
20831     reset : function(){
20832         this.setValue(this.resetValue);
20833         this.clearInvalid();
20834     },
20835
20836     // private
20837     initEvents : function(){
20838         // safari killled keypress - so keydown is now used..
20839         this.el.on("keydown" , this.fireKey,  this);
20840         this.el.on("focus", this.onFocus,  this);
20841         this.el.on("blur", this.onBlur,  this);
20842         this.el.relayEvent('keyup', this);
20843
20844         // reference to original value for reset
20845         this.originalValue = this.getValue();
20846         this.resetValue =  this.getValue();
20847     },
20848
20849     // private
20850     onFocus : function(){
20851         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20852             this.el.addClass(this.focusClass);
20853         }
20854         if(!this.hasFocus){
20855             this.hasFocus = true;
20856             this.startValue = this.getValue();
20857             this.fireEvent("focus", this);
20858         }
20859     },
20860
20861     beforeBlur : Roo.emptyFn,
20862
20863     // private
20864     onBlur : function(){
20865         this.beforeBlur();
20866         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20867             this.el.removeClass(this.focusClass);
20868         }
20869         this.hasFocus = false;
20870         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20871             this.validate();
20872         }
20873         var v = this.getValue();
20874         if(String(v) !== String(this.startValue)){
20875             this.fireEvent('change', this, v, this.startValue);
20876         }
20877         this.fireEvent("blur", this);
20878     },
20879
20880     /**
20881      * Returns whether or not the field value is currently valid
20882      * @param {Boolean} preventMark True to disable marking the field invalid
20883      * @return {Boolean} True if the value is valid, else false
20884      */
20885     isValid : function(preventMark){
20886         if(this.disabled){
20887             return true;
20888         }
20889         var restore = this.preventMark;
20890         this.preventMark = preventMark === true;
20891         var v = this.validateValue(this.processValue(this.getRawValue()));
20892         this.preventMark = restore;
20893         return v;
20894     },
20895
20896     /**
20897      * Validates the field value
20898      * @return {Boolean} True if the value is valid, else false
20899      */
20900     validate : function(){
20901         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20902             this.clearInvalid();
20903             return true;
20904         }
20905         return false;
20906     },
20907
20908     processValue : function(value){
20909         return value;
20910     },
20911
20912     // private
20913     // Subclasses should provide the validation implementation by overriding this
20914     validateValue : function(value){
20915         return true;
20916     },
20917
20918     /**
20919      * Mark this field as invalid
20920      * @param {String} msg The validation message
20921      */
20922     markInvalid : function(msg){
20923         if(!this.rendered || this.preventMark){ // not rendered
20924             return;
20925         }
20926         
20927         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20928         
20929         obj.el.addClass(this.invalidClass);
20930         msg = msg || this.invalidText;
20931         switch(this.msgTarget){
20932             case 'qtip':
20933                 obj.el.dom.qtip = msg;
20934                 obj.el.dom.qclass = 'x-form-invalid-tip';
20935                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20936                     Roo.QuickTips.enable();
20937                 }
20938                 break;
20939             case 'title':
20940                 this.el.dom.title = msg;
20941                 break;
20942             case 'under':
20943                 if(!this.errorEl){
20944                     var elp = this.el.findParent('.x-form-element', 5, true);
20945                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20946                     this.errorEl.setWidth(elp.getWidth(true)-20);
20947                 }
20948                 this.errorEl.update(msg);
20949                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20950                 break;
20951             case 'side':
20952                 if(!this.errorIcon){
20953                     var elp = this.el.findParent('.x-form-element', 5, true);
20954                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20955                 }
20956                 this.alignErrorIcon();
20957                 this.errorIcon.dom.qtip = msg;
20958                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20959                 this.errorIcon.show();
20960                 this.on('resize', this.alignErrorIcon, this);
20961                 break;
20962             default:
20963                 var t = Roo.getDom(this.msgTarget);
20964                 t.innerHTML = msg;
20965                 t.style.display = this.msgDisplay;
20966                 break;
20967         }
20968         this.fireEvent('invalid', this, msg);
20969     },
20970
20971     // private
20972     alignErrorIcon : function(){
20973         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20974     },
20975
20976     /**
20977      * Clear any invalid styles/messages for this field
20978      */
20979     clearInvalid : function(){
20980         if(!this.rendered || this.preventMark){ // not rendered
20981             return;
20982         }
20983         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20984         
20985         obj.el.removeClass(this.invalidClass);
20986         switch(this.msgTarget){
20987             case 'qtip':
20988                 obj.el.dom.qtip = '';
20989                 break;
20990             case 'title':
20991                 this.el.dom.title = '';
20992                 break;
20993             case 'under':
20994                 if(this.errorEl){
20995                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
20996                 }
20997                 break;
20998             case 'side':
20999                 if(this.errorIcon){
21000                     this.errorIcon.dom.qtip = '';
21001                     this.errorIcon.hide();
21002                     this.un('resize', this.alignErrorIcon, this);
21003                 }
21004                 break;
21005             default:
21006                 var t = Roo.getDom(this.msgTarget);
21007                 t.innerHTML = '';
21008                 t.style.display = 'none';
21009                 break;
21010         }
21011         this.fireEvent('valid', this);
21012     },
21013
21014     /**
21015      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21016      * @return {Mixed} value The field value
21017      */
21018     getRawValue : function(){
21019         var v = this.el.getValue();
21020         
21021         return v;
21022     },
21023
21024     /**
21025      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21026      * @return {Mixed} value The field value
21027      */
21028     getValue : function(){
21029         var v = this.el.getValue();
21030          
21031         return v;
21032     },
21033
21034     /**
21035      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21036      * @param {Mixed} value The value to set
21037      */
21038     setRawValue : function(v){
21039         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21040     },
21041
21042     /**
21043      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21044      * @param {Mixed} value The value to set
21045      */
21046     setValue : function(v){
21047         this.value = v;
21048         if(this.rendered){
21049             this.el.dom.value = (v === null || v === undefined ? '' : v);
21050              this.validate();
21051         }
21052     },
21053
21054     adjustSize : function(w, h){
21055         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21056         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21057         return s;
21058     },
21059
21060     adjustWidth : function(tag, w){
21061         tag = tag.toLowerCase();
21062         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21063             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21064                 if(tag == 'input'){
21065                     return w + 2;
21066                 }
21067                 if(tag == 'textarea'){
21068                     return w-2;
21069                 }
21070             }else if(Roo.isOpera){
21071                 if(tag == 'input'){
21072                     return w + 2;
21073                 }
21074                 if(tag == 'textarea'){
21075                     return w-2;
21076                 }
21077             }
21078         }
21079         return w;
21080     }
21081 });
21082
21083
21084 // anything other than normal should be considered experimental
21085 Roo.form.Field.msgFx = {
21086     normal : {
21087         show: function(msgEl, f){
21088             msgEl.setDisplayed('block');
21089         },
21090
21091         hide : function(msgEl, f){
21092             msgEl.setDisplayed(false).update('');
21093         }
21094     },
21095
21096     slide : {
21097         show: function(msgEl, f){
21098             msgEl.slideIn('t', {stopFx:true});
21099         },
21100
21101         hide : function(msgEl, f){
21102             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21103         }
21104     },
21105
21106     slideRight : {
21107         show: function(msgEl, f){
21108             msgEl.fixDisplay();
21109             msgEl.alignTo(f.el, 'tl-tr');
21110             msgEl.slideIn('l', {stopFx:true});
21111         },
21112
21113         hide : function(msgEl, f){
21114             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21115         }
21116     }
21117 };/*
21118  * Based on:
21119  * Ext JS Library 1.1.1
21120  * Copyright(c) 2006-2007, Ext JS, LLC.
21121  *
21122  * Originally Released Under LGPL - original licence link has changed is not relivant.
21123  *
21124  * Fork - LGPL
21125  * <script type="text/javascript">
21126  */
21127  
21128
21129 /**
21130  * @class Roo.form.TextField
21131  * @extends Roo.form.Field
21132  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21133  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21134  * @constructor
21135  * Creates a new TextField
21136  * @param {Object} config Configuration options
21137  */
21138 Roo.form.TextField = function(config){
21139     Roo.form.TextField.superclass.constructor.call(this, config);
21140     this.addEvents({
21141         /**
21142          * @event autosize
21143          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21144          * according to the default logic, but this event provides a hook for the developer to apply additional
21145          * logic at runtime to resize the field if needed.
21146              * @param {Roo.form.Field} this This text field
21147              * @param {Number} width The new field width
21148              */
21149         autosize : true
21150     });
21151 };
21152
21153 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21154     /**
21155      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21156      */
21157     grow : false,
21158     /**
21159      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21160      */
21161     growMin : 30,
21162     /**
21163      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21164      */
21165     growMax : 800,
21166     /**
21167      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21168      */
21169     vtype : null,
21170     /**
21171      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21172      */
21173     maskRe : null,
21174     /**
21175      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21176      */
21177     disableKeyFilter : false,
21178     /**
21179      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21180      */
21181     allowBlank : true,
21182     /**
21183      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21184      */
21185     minLength : 0,
21186     /**
21187      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21188      */
21189     maxLength : Number.MAX_VALUE,
21190     /**
21191      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21192      */
21193     minLengthText : "The minimum length for this field is {0}",
21194     /**
21195      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21196      */
21197     maxLengthText : "The maximum length for this field is {0}",
21198     /**
21199      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21200      */
21201     selectOnFocus : false,
21202     /**
21203      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21204      */
21205     blankText : "This field is required",
21206     /**
21207      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21208      * If available, this function will be called only after the basic validators all return true, and will be passed the
21209      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21210      */
21211     validator : null,
21212     /**
21213      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21214      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21215      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21216      */
21217     regex : null,
21218     /**
21219      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21220      */
21221     regexText : "",
21222     /**
21223      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21224      */
21225     emptyText : null,
21226    
21227
21228     // private
21229     initEvents : function()
21230     {
21231         if (this.emptyText) {
21232             this.el.attr('placeholder', this.emptyText);
21233         }
21234         
21235         Roo.form.TextField.superclass.initEvents.call(this);
21236         if(this.validationEvent == 'keyup'){
21237             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21238             this.el.on('keyup', this.filterValidation, this);
21239         }
21240         else if(this.validationEvent !== false){
21241             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21242         }
21243         
21244         if(this.selectOnFocus){
21245             this.on("focus", this.preFocus, this);
21246             
21247         }
21248         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21249             this.el.on("keypress", this.filterKeys, this);
21250         }
21251         if(this.grow){
21252             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21253             this.el.on("click", this.autoSize,  this);
21254         }
21255         if(this.el.is('input[type=password]') && Roo.isSafari){
21256             this.el.on('keydown', this.SafariOnKeyDown, this);
21257         }
21258     },
21259
21260     processValue : function(value){
21261         if(this.stripCharsRe){
21262             var newValue = value.replace(this.stripCharsRe, '');
21263             if(newValue !== value){
21264                 this.setRawValue(newValue);
21265                 return newValue;
21266             }
21267         }
21268         return value;
21269     },
21270
21271     filterValidation : function(e){
21272         if(!e.isNavKeyPress()){
21273             this.validationTask.delay(this.validationDelay);
21274         }
21275     },
21276
21277     // private
21278     onKeyUp : function(e){
21279         if(!e.isNavKeyPress()){
21280             this.autoSize();
21281         }
21282     },
21283
21284     /**
21285      * Resets the current field value to the originally-loaded value and clears any validation messages.
21286      *  
21287      */
21288     reset : function(){
21289         Roo.form.TextField.superclass.reset.call(this);
21290        
21291     },
21292
21293     
21294     // private
21295     preFocus : function(){
21296         
21297         if(this.selectOnFocus){
21298             this.el.dom.select();
21299         }
21300     },
21301
21302     
21303     // private
21304     filterKeys : function(e){
21305         var k = e.getKey();
21306         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21307             return;
21308         }
21309         var c = e.getCharCode(), cc = String.fromCharCode(c);
21310         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21311             return;
21312         }
21313         if(!this.maskRe.test(cc)){
21314             e.stopEvent();
21315         }
21316     },
21317
21318     setValue : function(v){
21319         
21320         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21321         
21322         this.autoSize();
21323     },
21324
21325     /**
21326      * Validates a value according to the field's validation rules and marks the field as invalid
21327      * if the validation fails
21328      * @param {Mixed} value The value to validate
21329      * @return {Boolean} True if the value is valid, else false
21330      */
21331     validateValue : function(value){
21332         if(value.length < 1)  { // if it's blank
21333              if(this.allowBlank){
21334                 this.clearInvalid();
21335                 return true;
21336              }else{
21337                 this.markInvalid(this.blankText);
21338                 return false;
21339              }
21340         }
21341         if(value.length < this.minLength){
21342             this.markInvalid(String.format(this.minLengthText, this.minLength));
21343             return false;
21344         }
21345         if(value.length > this.maxLength){
21346             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21347             return false;
21348         }
21349         if(this.vtype){
21350             var vt = Roo.form.VTypes;
21351             if(!vt[this.vtype](value, this)){
21352                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21353                 return false;
21354             }
21355         }
21356         if(typeof this.validator == "function"){
21357             var msg = this.validator(value);
21358             if(msg !== true){
21359                 this.markInvalid(msg);
21360                 return false;
21361             }
21362         }
21363         if(this.regex && !this.regex.test(value)){
21364             this.markInvalid(this.regexText);
21365             return false;
21366         }
21367         return true;
21368     },
21369
21370     /**
21371      * Selects text in this field
21372      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21373      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21374      */
21375     selectText : function(start, end){
21376         var v = this.getRawValue();
21377         if(v.length > 0){
21378             start = start === undefined ? 0 : start;
21379             end = end === undefined ? v.length : end;
21380             var d = this.el.dom;
21381             if(d.setSelectionRange){
21382                 d.setSelectionRange(start, end);
21383             }else if(d.createTextRange){
21384                 var range = d.createTextRange();
21385                 range.moveStart("character", start);
21386                 range.moveEnd("character", v.length-end);
21387                 range.select();
21388             }
21389         }
21390     },
21391
21392     /**
21393      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21394      * This only takes effect if grow = true, and fires the autosize event.
21395      */
21396     autoSize : function(){
21397         if(!this.grow || !this.rendered){
21398             return;
21399         }
21400         if(!this.metrics){
21401             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21402         }
21403         var el = this.el;
21404         var v = el.dom.value;
21405         var d = document.createElement('div');
21406         d.appendChild(document.createTextNode(v));
21407         v = d.innerHTML;
21408         d = null;
21409         v += "&#160;";
21410         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21411         this.el.setWidth(w);
21412         this.fireEvent("autosize", this, w);
21413     },
21414     
21415     // private
21416     SafariOnKeyDown : function(event)
21417     {
21418         // this is a workaround for a password hang bug on chrome/ webkit.
21419         
21420         var isSelectAll = false;
21421         
21422         if(this.el.dom.selectionEnd > 0){
21423             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21424         }
21425         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21426             event.preventDefault();
21427             this.setValue('');
21428             return;
21429         }
21430         
21431         if(isSelectAll){ // backspace and delete key
21432             
21433             event.preventDefault();
21434             // this is very hacky as keydown always get's upper case.
21435             //
21436             var cc = String.fromCharCode(event.getCharCode());
21437             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21438             
21439         }
21440         
21441         
21442     }
21443 });/*
21444  * Based on:
21445  * Ext JS Library 1.1.1
21446  * Copyright(c) 2006-2007, Ext JS, LLC.
21447  *
21448  * Originally Released Under LGPL - original licence link has changed is not relivant.
21449  *
21450  * Fork - LGPL
21451  * <script type="text/javascript">
21452  */
21453  
21454 /**
21455  * @class Roo.form.Hidden
21456  * @extends Roo.form.TextField
21457  * Simple Hidden element used on forms 
21458  * 
21459  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21460  * 
21461  * @constructor
21462  * Creates a new Hidden form element.
21463  * @param {Object} config Configuration options
21464  */
21465
21466
21467
21468 // easy hidden field...
21469 Roo.form.Hidden = function(config){
21470     Roo.form.Hidden.superclass.constructor.call(this, config);
21471 };
21472   
21473 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21474     fieldLabel:      '',
21475     inputType:      'hidden',
21476     width:          50,
21477     allowBlank:     true,
21478     labelSeparator: '',
21479     hidden:         true,
21480     itemCls :       'x-form-item-display-none'
21481
21482
21483 });
21484
21485
21486 /*
21487  * Based on:
21488  * Ext JS Library 1.1.1
21489  * Copyright(c) 2006-2007, Ext JS, LLC.
21490  *
21491  * Originally Released Under LGPL - original licence link has changed is not relivant.
21492  *
21493  * Fork - LGPL
21494  * <script type="text/javascript">
21495  */
21496  
21497 /**
21498  * @class Roo.form.TriggerField
21499  * @extends Roo.form.TextField
21500  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21501  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21502  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21503  * for which you can provide a custom implementation.  For example:
21504  * <pre><code>
21505 var trigger = new Roo.form.TriggerField();
21506 trigger.onTriggerClick = myTriggerFn;
21507 trigger.applyTo('my-field');
21508 </code></pre>
21509  *
21510  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21511  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21512  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21513  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21514  * @constructor
21515  * Create a new TriggerField.
21516  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21517  * to the base TextField)
21518  */
21519 Roo.form.TriggerField = function(config){
21520     this.mimicing = false;
21521     Roo.form.TriggerField.superclass.constructor.call(this, config);
21522 };
21523
21524 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21525     /**
21526      * @cfg {String} triggerClass A CSS class to apply to the trigger
21527      */
21528     /**
21529      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21530      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21531      */
21532     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21533     /**
21534      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21535      */
21536     hideTrigger:false,
21537
21538     /** @cfg {Boolean} grow @hide */
21539     /** @cfg {Number} growMin @hide */
21540     /** @cfg {Number} growMax @hide */
21541
21542     /**
21543      * @hide 
21544      * @method
21545      */
21546     autoSize: Roo.emptyFn,
21547     // private
21548     monitorTab : true,
21549     // private
21550     deferHeight : true,
21551
21552     
21553     actionMode : 'wrap',
21554     // private
21555     onResize : function(w, h){
21556         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21557         if(typeof w == 'number'){
21558             var x = w - this.trigger.getWidth();
21559             this.el.setWidth(this.adjustWidth('input', x));
21560             this.trigger.setStyle('left', x+'px');
21561         }
21562     },
21563
21564     // private
21565     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21566
21567     // private
21568     getResizeEl : function(){
21569         return this.wrap;
21570     },
21571
21572     // private
21573     getPositionEl : function(){
21574         return this.wrap;
21575     },
21576
21577     // private
21578     alignErrorIcon : function(){
21579         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21580     },
21581
21582     // private
21583     onRender : function(ct, position){
21584         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21585         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21586         this.trigger = this.wrap.createChild(this.triggerConfig ||
21587                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21588         if(this.hideTrigger){
21589             this.trigger.setDisplayed(false);
21590         }
21591         this.initTrigger();
21592         if(!this.width){
21593             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21594         }
21595     },
21596
21597     // private
21598     initTrigger : function(){
21599         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21600         this.trigger.addClassOnOver('x-form-trigger-over');
21601         this.trigger.addClassOnClick('x-form-trigger-click');
21602     },
21603
21604     // private
21605     onDestroy : function(){
21606         if(this.trigger){
21607             this.trigger.removeAllListeners();
21608             this.trigger.remove();
21609         }
21610         if(this.wrap){
21611             this.wrap.remove();
21612         }
21613         Roo.form.TriggerField.superclass.onDestroy.call(this);
21614     },
21615
21616     // private
21617     onFocus : function(){
21618         Roo.form.TriggerField.superclass.onFocus.call(this);
21619         if(!this.mimicing){
21620             this.wrap.addClass('x-trigger-wrap-focus');
21621             this.mimicing = true;
21622             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21623             if(this.monitorTab){
21624                 this.el.on("keydown", this.checkTab, this);
21625             }
21626         }
21627     },
21628
21629     // private
21630     checkTab : function(e){
21631         if(e.getKey() == e.TAB){
21632             this.triggerBlur();
21633         }
21634     },
21635
21636     // private
21637     onBlur : function(){
21638         // do nothing
21639     },
21640
21641     // private
21642     mimicBlur : function(e, t){
21643         if(!this.wrap.contains(t) && this.validateBlur()){
21644             this.triggerBlur();
21645         }
21646     },
21647
21648     // private
21649     triggerBlur : function(){
21650         this.mimicing = false;
21651         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21652         if(this.monitorTab){
21653             this.el.un("keydown", this.checkTab, this);
21654         }
21655         this.wrap.removeClass('x-trigger-wrap-focus');
21656         Roo.form.TriggerField.superclass.onBlur.call(this);
21657     },
21658
21659     // private
21660     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21661     validateBlur : function(e, t){
21662         return true;
21663     },
21664
21665     // private
21666     onDisable : function(){
21667         Roo.form.TriggerField.superclass.onDisable.call(this);
21668         if(this.wrap){
21669             this.wrap.addClass('x-item-disabled');
21670         }
21671     },
21672
21673     // private
21674     onEnable : function(){
21675         Roo.form.TriggerField.superclass.onEnable.call(this);
21676         if(this.wrap){
21677             this.wrap.removeClass('x-item-disabled');
21678         }
21679     },
21680
21681     // private
21682     onShow : function(){
21683         var ae = this.getActionEl();
21684         
21685         if(ae){
21686             ae.dom.style.display = '';
21687             ae.dom.style.visibility = 'visible';
21688         }
21689     },
21690
21691     // private
21692     
21693     onHide : function(){
21694         var ae = this.getActionEl();
21695         ae.dom.style.display = 'none';
21696     },
21697
21698     /**
21699      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21700      * by an implementing function.
21701      * @method
21702      * @param {EventObject} e
21703      */
21704     onTriggerClick : Roo.emptyFn
21705 });
21706
21707 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21708 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21709 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21710 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21711     initComponent : function(){
21712         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21713
21714         this.triggerConfig = {
21715             tag:'span', cls:'x-form-twin-triggers', cn:[
21716             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21717             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21718         ]};
21719     },
21720
21721     getTrigger : function(index){
21722         return this.triggers[index];
21723     },
21724
21725     initTrigger : function(){
21726         var ts = this.trigger.select('.x-form-trigger', true);
21727         this.wrap.setStyle('overflow', 'hidden');
21728         var triggerField = this;
21729         ts.each(function(t, all, index){
21730             t.hide = function(){
21731                 var w = triggerField.wrap.getWidth();
21732                 this.dom.style.display = 'none';
21733                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21734             };
21735             t.show = function(){
21736                 var w = triggerField.wrap.getWidth();
21737                 this.dom.style.display = '';
21738                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21739             };
21740             var triggerIndex = 'Trigger'+(index+1);
21741
21742             if(this['hide'+triggerIndex]){
21743                 t.dom.style.display = 'none';
21744             }
21745             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21746             t.addClassOnOver('x-form-trigger-over');
21747             t.addClassOnClick('x-form-trigger-click');
21748         }, this);
21749         this.triggers = ts.elements;
21750     },
21751
21752     onTrigger1Click : Roo.emptyFn,
21753     onTrigger2Click : Roo.emptyFn
21754 });/*
21755  * Based on:
21756  * Ext JS Library 1.1.1
21757  * Copyright(c) 2006-2007, Ext JS, LLC.
21758  *
21759  * Originally Released Under LGPL - original licence link has changed is not relivant.
21760  *
21761  * Fork - LGPL
21762  * <script type="text/javascript">
21763  */
21764  
21765 /**
21766  * @class Roo.form.TextArea
21767  * @extends Roo.form.TextField
21768  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21769  * support for auto-sizing.
21770  * @constructor
21771  * Creates a new TextArea
21772  * @param {Object} config Configuration options
21773  */
21774 Roo.form.TextArea = function(config){
21775     Roo.form.TextArea.superclass.constructor.call(this, config);
21776     // these are provided exchanges for backwards compat
21777     // minHeight/maxHeight were replaced by growMin/growMax to be
21778     // compatible with TextField growing config values
21779     if(this.minHeight !== undefined){
21780         this.growMin = this.minHeight;
21781     }
21782     if(this.maxHeight !== undefined){
21783         this.growMax = this.maxHeight;
21784     }
21785 };
21786
21787 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21788     /**
21789      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21790      */
21791     growMin : 60,
21792     /**
21793      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21794      */
21795     growMax: 1000,
21796     /**
21797      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21798      * in the field (equivalent to setting overflow: hidden, defaults to false)
21799      */
21800     preventScrollbars: false,
21801     /**
21802      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21803      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21804      */
21805
21806     // private
21807     onRender : function(ct, position){
21808         if(!this.el){
21809             this.defaultAutoCreate = {
21810                 tag: "textarea",
21811                 style:"width:300px;height:60px;",
21812                 autocomplete: "off"
21813             };
21814         }
21815         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21816         if(this.grow){
21817             this.textSizeEl = Roo.DomHelper.append(document.body, {
21818                 tag: "pre", cls: "x-form-grow-sizer"
21819             });
21820             if(this.preventScrollbars){
21821                 this.el.setStyle("overflow", "hidden");
21822             }
21823             this.el.setHeight(this.growMin);
21824         }
21825     },
21826
21827     onDestroy : function(){
21828         if(this.textSizeEl){
21829             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21830         }
21831         Roo.form.TextArea.superclass.onDestroy.call(this);
21832     },
21833
21834     // private
21835     onKeyUp : function(e){
21836         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21837             this.autoSize();
21838         }
21839     },
21840
21841     /**
21842      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21843      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21844      */
21845     autoSize : function(){
21846         if(!this.grow || !this.textSizeEl){
21847             return;
21848         }
21849         var el = this.el;
21850         var v = el.dom.value;
21851         var ts = this.textSizeEl;
21852
21853         ts.innerHTML = '';
21854         ts.appendChild(document.createTextNode(v));
21855         v = ts.innerHTML;
21856
21857         Roo.fly(ts).setWidth(this.el.getWidth());
21858         if(v.length < 1){
21859             v = "&#160;&#160;";
21860         }else{
21861             if(Roo.isIE){
21862                 v = v.replace(/\n/g, '<p>&#160;</p>');
21863             }
21864             v += "&#160;\n&#160;";
21865         }
21866         ts.innerHTML = v;
21867         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21868         if(h != this.lastHeight){
21869             this.lastHeight = h;
21870             this.el.setHeight(h);
21871             this.fireEvent("autosize", this, h);
21872         }
21873     }
21874 });/*
21875  * Based on:
21876  * Ext JS Library 1.1.1
21877  * Copyright(c) 2006-2007, Ext JS, LLC.
21878  *
21879  * Originally Released Under LGPL - original licence link has changed is not relivant.
21880  *
21881  * Fork - LGPL
21882  * <script type="text/javascript">
21883  */
21884  
21885
21886 /**
21887  * @class Roo.form.NumberField
21888  * @extends Roo.form.TextField
21889  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21890  * @constructor
21891  * Creates a new NumberField
21892  * @param {Object} config Configuration options
21893  */
21894 Roo.form.NumberField = function(config){
21895     Roo.form.NumberField.superclass.constructor.call(this, config);
21896 };
21897
21898 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21899     /**
21900      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21901      */
21902     fieldClass: "x-form-field x-form-num-field",
21903     /**
21904      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21905      */
21906     allowDecimals : true,
21907     /**
21908      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21909      */
21910     decimalSeparator : ".",
21911     /**
21912      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21913      */
21914     decimalPrecision : 2,
21915     /**
21916      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21917      */
21918     allowNegative : true,
21919     /**
21920      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21921      */
21922     minValue : Number.NEGATIVE_INFINITY,
21923     /**
21924      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21925      */
21926     maxValue : Number.MAX_VALUE,
21927     /**
21928      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21929      */
21930     minText : "The minimum value for this field is {0}",
21931     /**
21932      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21933      */
21934     maxText : "The maximum value for this field is {0}",
21935     /**
21936      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21937      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21938      */
21939     nanText : "{0} is not a valid number",
21940
21941     // private
21942     initEvents : function(){
21943         Roo.form.NumberField.superclass.initEvents.call(this);
21944         var allowed = "0123456789";
21945         if(this.allowDecimals){
21946             allowed += this.decimalSeparator;
21947         }
21948         if(this.allowNegative){
21949             allowed += "-";
21950         }
21951         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21952         var keyPress = function(e){
21953             var k = e.getKey();
21954             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21955                 return;
21956             }
21957             var c = e.getCharCode();
21958             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21959                 e.stopEvent();
21960             }
21961         };
21962         this.el.on("keypress", keyPress, this);
21963     },
21964
21965     // private
21966     validateValue : function(value){
21967         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21968             return false;
21969         }
21970         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21971              return true;
21972         }
21973         var num = this.parseValue(value);
21974         if(isNaN(num)){
21975             this.markInvalid(String.format(this.nanText, value));
21976             return false;
21977         }
21978         if(num < this.minValue){
21979             this.markInvalid(String.format(this.minText, this.minValue));
21980             return false;
21981         }
21982         if(num > this.maxValue){
21983             this.markInvalid(String.format(this.maxText, this.maxValue));
21984             return false;
21985         }
21986         return true;
21987     },
21988
21989     getValue : function(){
21990         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
21991     },
21992
21993     // private
21994     parseValue : function(value){
21995         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
21996         return isNaN(value) ? '' : value;
21997     },
21998
21999     // private
22000     fixPrecision : function(value){
22001         var nan = isNaN(value);
22002         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22003             return nan ? '' : value;
22004         }
22005         return parseFloat(value).toFixed(this.decimalPrecision);
22006     },
22007
22008     setValue : function(v){
22009         v = this.fixPrecision(v);
22010         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22011     },
22012
22013     // private
22014     decimalPrecisionFcn : function(v){
22015         return Math.floor(v);
22016     },
22017
22018     beforeBlur : function(){
22019         var v = this.parseValue(this.getRawValue());
22020         if(v){
22021             this.setValue(v);
22022         }
22023     }
22024 });/*
22025  * Based on:
22026  * Ext JS Library 1.1.1
22027  * Copyright(c) 2006-2007, Ext JS, LLC.
22028  *
22029  * Originally Released Under LGPL - original licence link has changed is not relivant.
22030  *
22031  * Fork - LGPL
22032  * <script type="text/javascript">
22033  */
22034  
22035 /**
22036  * @class Roo.form.DateField
22037  * @extends Roo.form.TriggerField
22038  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22039 * @constructor
22040 * Create a new DateField
22041 * @param {Object} config
22042  */
22043 Roo.form.DateField = function(config){
22044     Roo.form.DateField.superclass.constructor.call(this, config);
22045     
22046       this.addEvents({
22047          
22048         /**
22049          * @event select
22050          * Fires when a date is selected
22051              * @param {Roo.form.DateField} combo This combo box
22052              * @param {Date} date The date selected
22053              */
22054         'select' : true
22055          
22056     });
22057     
22058     
22059     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22060     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22061     this.ddMatch = null;
22062     if(this.disabledDates){
22063         var dd = this.disabledDates;
22064         var re = "(?:";
22065         for(var i = 0; i < dd.length; i++){
22066             re += dd[i];
22067             if(i != dd.length-1) re += "|";
22068         }
22069         this.ddMatch = new RegExp(re + ")");
22070     }
22071 };
22072
22073 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22074     /**
22075      * @cfg {String} format
22076      * The default date format string which can be overriden for localization support.  The format must be
22077      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22078      */
22079     format : "m/d/y",
22080     /**
22081      * @cfg {String} altFormats
22082      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22083      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22084      */
22085     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22086     /**
22087      * @cfg {Array} disabledDays
22088      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22089      */
22090     disabledDays : null,
22091     /**
22092      * @cfg {String} disabledDaysText
22093      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22094      */
22095     disabledDaysText : "Disabled",
22096     /**
22097      * @cfg {Array} disabledDates
22098      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22099      * expression so they are very powerful. Some examples:
22100      * <ul>
22101      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22102      * <li>["03/08", "09/16"] would disable those days for every year</li>
22103      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22104      * <li>["03/../2006"] would disable every day in March 2006</li>
22105      * <li>["^03"] would disable every day in every March</li>
22106      * </ul>
22107      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22108      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22109      */
22110     disabledDates : null,
22111     /**
22112      * @cfg {String} disabledDatesText
22113      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22114      */
22115     disabledDatesText : "Disabled",
22116     /**
22117      * @cfg {Date/String} minValue
22118      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22119      * valid format (defaults to null).
22120      */
22121     minValue : null,
22122     /**
22123      * @cfg {Date/String} maxValue
22124      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22125      * valid format (defaults to null).
22126      */
22127     maxValue : null,
22128     /**
22129      * @cfg {String} minText
22130      * The error text to display when the date in the cell is before minValue (defaults to
22131      * 'The date in this field must be after {minValue}').
22132      */
22133     minText : "The date in this field must be equal to or after {0}",
22134     /**
22135      * @cfg {String} maxText
22136      * The error text to display when the date in the cell is after maxValue (defaults to
22137      * 'The date in this field must be before {maxValue}').
22138      */
22139     maxText : "The date in this field must be equal to or before {0}",
22140     /**
22141      * @cfg {String} invalidText
22142      * The error text to display when the date in the field is invalid (defaults to
22143      * '{value} is not a valid date - it must be in the format {format}').
22144      */
22145     invalidText : "{0} is not a valid date - it must be in the format {1}",
22146     /**
22147      * @cfg {String} triggerClass
22148      * An additional CSS class used to style the trigger button.  The trigger will always get the
22149      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22150      * which displays a calendar icon).
22151      */
22152     triggerClass : 'x-form-date-trigger',
22153     
22154
22155     /**
22156      * @cfg {Boolean} useIso
22157      * if enabled, then the date field will use a hidden field to store the 
22158      * real value as iso formated date. default (false)
22159      */ 
22160     useIso : false,
22161     /**
22162      * @cfg {String/Object} autoCreate
22163      * A DomHelper element spec, or true for a default element spec (defaults to
22164      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22165      */ 
22166     // private
22167     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22168     
22169     // private
22170     hiddenField: false,
22171     
22172     onRender : function(ct, position)
22173     {
22174         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22175         if (this.useIso) {
22176             //this.el.dom.removeAttribute('name'); 
22177             Roo.log("Changing name?");
22178             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22179             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22180                     'before', true);
22181             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22182             // prevent input submission
22183             this.hiddenName = this.name;
22184         }
22185             
22186             
22187     },
22188     
22189     // private
22190     validateValue : function(value)
22191     {
22192         value = this.formatDate(value);
22193         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22194             Roo.log('super failed');
22195             return false;
22196         }
22197         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22198              return true;
22199         }
22200         var svalue = value;
22201         value = this.parseDate(value);
22202         if(!value){
22203             Roo.log('parse date failed' + svalue);
22204             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22205             return false;
22206         }
22207         var time = value.getTime();
22208         if(this.minValue && time < this.minValue.getTime()){
22209             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22210             return false;
22211         }
22212         if(this.maxValue && time > this.maxValue.getTime()){
22213             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22214             return false;
22215         }
22216         if(this.disabledDays){
22217             var day = value.getDay();
22218             for(var i = 0; i < this.disabledDays.length; i++) {
22219                 if(day === this.disabledDays[i]){
22220                     this.markInvalid(this.disabledDaysText);
22221                     return false;
22222                 }
22223             }
22224         }
22225         var fvalue = this.formatDate(value);
22226         if(this.ddMatch && this.ddMatch.test(fvalue)){
22227             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22228             return false;
22229         }
22230         return true;
22231     },
22232
22233     // private
22234     // Provides logic to override the default TriggerField.validateBlur which just returns true
22235     validateBlur : function(){
22236         return !this.menu || !this.menu.isVisible();
22237     },
22238     
22239     getName: function()
22240     {
22241         // returns hidden if it's set..
22242         if (!this.rendered) {return ''};
22243         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22244         
22245     },
22246
22247     /**
22248      * Returns the current date value of the date field.
22249      * @return {Date} The date value
22250      */
22251     getValue : function(){
22252         
22253         return  this.hiddenField ?
22254                 this.hiddenField.value :
22255                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22256     },
22257
22258     /**
22259      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22260      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22261      * (the default format used is "m/d/y").
22262      * <br />Usage:
22263      * <pre><code>
22264 //All of these calls set the same date value (May 4, 2006)
22265
22266 //Pass a date object:
22267 var dt = new Date('5/4/06');
22268 dateField.setValue(dt);
22269
22270 //Pass a date string (default format):
22271 dateField.setValue('5/4/06');
22272
22273 //Pass a date string (custom format):
22274 dateField.format = 'Y-m-d';
22275 dateField.setValue('2006-5-4');
22276 </code></pre>
22277      * @param {String/Date} date The date or valid date string
22278      */
22279     setValue : function(date){
22280         if (this.hiddenField) {
22281             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22282         }
22283         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22284         // make sure the value field is always stored as a date..
22285         this.value = this.parseDate(date);
22286         
22287         
22288     },
22289
22290     // private
22291     parseDate : function(value){
22292         if(!value || value instanceof Date){
22293             return value;
22294         }
22295         var v = Date.parseDate(value, this.format);
22296          if (!v && this.useIso) {
22297             v = Date.parseDate(value, 'Y-m-d');
22298         }
22299         if(!v && this.altFormats){
22300             if(!this.altFormatsArray){
22301                 this.altFormatsArray = this.altFormats.split("|");
22302             }
22303             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22304                 v = Date.parseDate(value, this.altFormatsArray[i]);
22305             }
22306         }
22307         return v;
22308     },
22309
22310     // private
22311     formatDate : function(date, fmt){
22312         return (!date || !(date instanceof Date)) ?
22313                date : date.dateFormat(fmt || this.format);
22314     },
22315
22316     // private
22317     menuListeners : {
22318         select: function(m, d){
22319             
22320             this.setValue(d);
22321             this.fireEvent('select', this, d);
22322         },
22323         show : function(){ // retain focus styling
22324             this.onFocus();
22325         },
22326         hide : function(){
22327             this.focus.defer(10, this);
22328             var ml = this.menuListeners;
22329             this.menu.un("select", ml.select,  this);
22330             this.menu.un("show", ml.show,  this);
22331             this.menu.un("hide", ml.hide,  this);
22332         }
22333     },
22334
22335     // private
22336     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22337     onTriggerClick : function(){
22338         if(this.disabled){
22339             return;
22340         }
22341         if(this.menu == null){
22342             this.menu = new Roo.menu.DateMenu();
22343         }
22344         Roo.apply(this.menu.picker,  {
22345             showClear: this.allowBlank,
22346             minDate : this.minValue,
22347             maxDate : this.maxValue,
22348             disabledDatesRE : this.ddMatch,
22349             disabledDatesText : this.disabledDatesText,
22350             disabledDays : this.disabledDays,
22351             disabledDaysText : this.disabledDaysText,
22352             format : this.useIso ? 'Y-m-d' : this.format,
22353             minText : String.format(this.minText, this.formatDate(this.minValue)),
22354             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22355         });
22356         this.menu.on(Roo.apply({}, this.menuListeners, {
22357             scope:this
22358         }));
22359         this.menu.picker.setValue(this.getValue() || new Date());
22360         this.menu.show(this.el, "tl-bl?");
22361     },
22362
22363     beforeBlur : function(){
22364         var v = this.parseDate(this.getRawValue());
22365         if(v){
22366             this.setValue(v);
22367         }
22368     },
22369
22370     /*@
22371      * overide
22372      * 
22373      */
22374     isDirty : function() {
22375         if(this.disabled) {
22376             return false;
22377         }
22378         
22379         if(typeof(this.startValue) === 'undefined'){
22380             return false;
22381         }
22382         
22383         return String(this.getValue()) !== String(this.startValue);
22384         
22385     }
22386 });/*
22387  * Based on:
22388  * Ext JS Library 1.1.1
22389  * Copyright(c) 2006-2007, Ext JS, LLC.
22390  *
22391  * Originally Released Under LGPL - original licence link has changed is not relivant.
22392  *
22393  * Fork - LGPL
22394  * <script type="text/javascript">
22395  */
22396  
22397 /**
22398  * @class Roo.form.MonthField
22399  * @extends Roo.form.TriggerField
22400  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22401 * @constructor
22402 * Create a new MonthField
22403 * @param {Object} config
22404  */
22405 Roo.form.MonthField = function(config){
22406     
22407     Roo.form.MonthField.superclass.constructor.call(this, config);
22408     
22409       this.addEvents({
22410          
22411         /**
22412          * @event select
22413          * Fires when a date is selected
22414              * @param {Roo.form.MonthFieeld} combo This combo box
22415              * @param {Date} date The date selected
22416              */
22417         'select' : true
22418          
22419     });
22420     
22421     
22422     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22423     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22424     this.ddMatch = null;
22425     if(this.disabledDates){
22426         var dd = this.disabledDates;
22427         var re = "(?:";
22428         for(var i = 0; i < dd.length; i++){
22429             re += dd[i];
22430             if(i != dd.length-1) re += "|";
22431         }
22432         this.ddMatch = new RegExp(re + ")");
22433     }
22434 };
22435
22436 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22437     /**
22438      * @cfg {String} format
22439      * The default date format string which can be overriden for localization support.  The format must be
22440      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22441      */
22442     format : "M Y",
22443     /**
22444      * @cfg {String} altFormats
22445      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22446      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22447      */
22448     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22449     /**
22450      * @cfg {Array} disabledDays
22451      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22452      */
22453     disabledDays : [0,1,2,3,4,5,6],
22454     /**
22455      * @cfg {String} disabledDaysText
22456      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22457      */
22458     disabledDaysText : "Disabled",
22459     /**
22460      * @cfg {Array} disabledDates
22461      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22462      * expression so they are very powerful. Some examples:
22463      * <ul>
22464      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22465      * <li>["03/08", "09/16"] would disable those days for every year</li>
22466      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22467      * <li>["03/../2006"] would disable every day in March 2006</li>
22468      * <li>["^03"] would disable every day in every March</li>
22469      * </ul>
22470      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22471      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22472      */
22473     disabledDates : null,
22474     /**
22475      * @cfg {String} disabledDatesText
22476      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22477      */
22478     disabledDatesText : "Disabled",
22479     /**
22480      * @cfg {Date/String} minValue
22481      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22482      * valid format (defaults to null).
22483      */
22484     minValue : null,
22485     /**
22486      * @cfg {Date/String} maxValue
22487      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22488      * valid format (defaults to null).
22489      */
22490     maxValue : null,
22491     /**
22492      * @cfg {String} minText
22493      * The error text to display when the date in the cell is before minValue (defaults to
22494      * 'The date in this field must be after {minValue}').
22495      */
22496     minText : "The date in this field must be equal to or after {0}",
22497     /**
22498      * @cfg {String} maxTextf
22499      * The error text to display when the date in the cell is after maxValue (defaults to
22500      * 'The date in this field must be before {maxValue}').
22501      */
22502     maxText : "The date in this field must be equal to or before {0}",
22503     /**
22504      * @cfg {String} invalidText
22505      * The error text to display when the date in the field is invalid (defaults to
22506      * '{value} is not a valid date - it must be in the format {format}').
22507      */
22508     invalidText : "{0} is not a valid date - it must be in the format {1}",
22509     /**
22510      * @cfg {String} triggerClass
22511      * An additional CSS class used to style the trigger button.  The trigger will always get the
22512      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22513      * which displays a calendar icon).
22514      */
22515     triggerClass : 'x-form-date-trigger',
22516     
22517
22518     /**
22519      * @cfg {Boolean} useIso
22520      * if enabled, then the date field will use a hidden field to store the 
22521      * real value as iso formated date. default (true)
22522      */ 
22523     useIso : true,
22524     /**
22525      * @cfg {String/Object} autoCreate
22526      * A DomHelper element spec, or true for a default element spec (defaults to
22527      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22528      */ 
22529     // private
22530     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22531     
22532     // private
22533     hiddenField: false,
22534     
22535     hideMonthPicker : false,
22536     
22537     onRender : function(ct, position)
22538     {
22539         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22540         if (this.useIso) {
22541             this.el.dom.removeAttribute('name'); 
22542             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22543                     'before', true);
22544             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22545             // prevent input submission
22546             this.hiddenName = this.name;
22547         }
22548             
22549             
22550     },
22551     
22552     // private
22553     validateValue : function(value)
22554     {
22555         value = this.formatDate(value);
22556         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22557             return false;
22558         }
22559         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22560              return true;
22561         }
22562         var svalue = value;
22563         value = this.parseDate(value);
22564         if(!value){
22565             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22566             return false;
22567         }
22568         var time = value.getTime();
22569         if(this.minValue && time < this.minValue.getTime()){
22570             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22571             return false;
22572         }
22573         if(this.maxValue && time > this.maxValue.getTime()){
22574             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22575             return false;
22576         }
22577         /*if(this.disabledDays){
22578             var day = value.getDay();
22579             for(var i = 0; i < this.disabledDays.length; i++) {
22580                 if(day === this.disabledDays[i]){
22581                     this.markInvalid(this.disabledDaysText);
22582                     return false;
22583                 }
22584             }
22585         }
22586         */
22587         var fvalue = this.formatDate(value);
22588         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22589             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22590             return false;
22591         }
22592         */
22593         return true;
22594     },
22595
22596     // private
22597     // Provides logic to override the default TriggerField.validateBlur which just returns true
22598     validateBlur : function(){
22599         return !this.menu || !this.menu.isVisible();
22600     },
22601
22602     /**
22603      * Returns the current date value of the date field.
22604      * @return {Date} The date value
22605      */
22606     getValue : function(){
22607         
22608         
22609         
22610         return  this.hiddenField ?
22611                 this.hiddenField.value :
22612                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22613     },
22614
22615     /**
22616      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22617      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22618      * (the default format used is "m/d/y").
22619      * <br />Usage:
22620      * <pre><code>
22621 //All of these calls set the same date value (May 4, 2006)
22622
22623 //Pass a date object:
22624 var dt = new Date('5/4/06');
22625 monthField.setValue(dt);
22626
22627 //Pass a date string (default format):
22628 monthField.setValue('5/4/06');
22629
22630 //Pass a date string (custom format):
22631 monthField.format = 'Y-m-d';
22632 monthField.setValue('2006-5-4');
22633 </code></pre>
22634      * @param {String/Date} date The date or valid date string
22635      */
22636     setValue : function(date){
22637         Roo.log('month setValue' + date);
22638         // can only be first of month..
22639         
22640         var val = this.parseDate(date);
22641         
22642         if (this.hiddenField) {
22643             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22644         }
22645         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22646         this.value = this.parseDate(date);
22647     },
22648
22649     // private
22650     parseDate : function(value){
22651         if(!value || value instanceof Date){
22652             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22653             return value;
22654         }
22655         var v = Date.parseDate(value, this.format);
22656         if (!v && this.useIso) {
22657             v = Date.parseDate(value, 'Y-m-d');
22658         }
22659         if (v) {
22660             // 
22661             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22662         }
22663         
22664         
22665         if(!v && this.altFormats){
22666             if(!this.altFormatsArray){
22667                 this.altFormatsArray = this.altFormats.split("|");
22668             }
22669             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22670                 v = Date.parseDate(value, this.altFormatsArray[i]);
22671             }
22672         }
22673         return v;
22674     },
22675
22676     // private
22677     formatDate : function(date, fmt){
22678         return (!date || !(date instanceof Date)) ?
22679                date : date.dateFormat(fmt || this.format);
22680     },
22681
22682     // private
22683     menuListeners : {
22684         select: function(m, d){
22685             this.setValue(d);
22686             this.fireEvent('select', this, d);
22687         },
22688         show : function(){ // retain focus styling
22689             this.onFocus();
22690         },
22691         hide : function(){
22692             this.focus.defer(10, this);
22693             var ml = this.menuListeners;
22694             this.menu.un("select", ml.select,  this);
22695             this.menu.un("show", ml.show,  this);
22696             this.menu.un("hide", ml.hide,  this);
22697         }
22698     },
22699     // private
22700     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22701     onTriggerClick : function(){
22702         if(this.disabled){
22703             return;
22704         }
22705         if(this.menu == null){
22706             this.menu = new Roo.menu.DateMenu();
22707            
22708         }
22709         
22710         Roo.apply(this.menu.picker,  {
22711             
22712             showClear: this.allowBlank,
22713             minDate : this.minValue,
22714             maxDate : this.maxValue,
22715             disabledDatesRE : this.ddMatch,
22716             disabledDatesText : this.disabledDatesText,
22717             
22718             format : this.useIso ? 'Y-m-d' : this.format,
22719             minText : String.format(this.minText, this.formatDate(this.minValue)),
22720             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22721             
22722         });
22723          this.menu.on(Roo.apply({}, this.menuListeners, {
22724             scope:this
22725         }));
22726        
22727         
22728         var m = this.menu;
22729         var p = m.picker;
22730         
22731         // hide month picker get's called when we called by 'before hide';
22732         
22733         var ignorehide = true;
22734         p.hideMonthPicker  = function(disableAnim){
22735             if (ignorehide) {
22736                 return;
22737             }
22738              if(this.monthPicker){
22739                 Roo.log("hideMonthPicker called");
22740                 if(disableAnim === true){
22741                     this.monthPicker.hide();
22742                 }else{
22743                     this.monthPicker.slideOut('t', {duration:.2});
22744                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22745                     p.fireEvent("select", this, this.value);
22746                     m.hide();
22747                 }
22748             }
22749         }
22750         
22751         Roo.log('picker set value');
22752         Roo.log(this.getValue());
22753         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22754         m.show(this.el, 'tl-bl?');
22755         ignorehide  = false;
22756         // this will trigger hideMonthPicker..
22757         
22758         
22759         // hidden the day picker
22760         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22761         
22762         
22763         
22764       
22765         
22766         p.showMonthPicker.defer(100, p);
22767     
22768         
22769        
22770     },
22771
22772     beforeBlur : function(){
22773         var v = this.parseDate(this.getRawValue());
22774         if(v){
22775             this.setValue(v);
22776         }
22777     }
22778
22779     /** @cfg {Boolean} grow @hide */
22780     /** @cfg {Number} growMin @hide */
22781     /** @cfg {Number} growMax @hide */
22782     /**
22783      * @hide
22784      * @method autoSize
22785      */
22786 });/*
22787  * Based on:
22788  * Ext JS Library 1.1.1
22789  * Copyright(c) 2006-2007, Ext JS, LLC.
22790  *
22791  * Originally Released Under LGPL - original licence link has changed is not relivant.
22792  *
22793  * Fork - LGPL
22794  * <script type="text/javascript">
22795  */
22796  
22797
22798 /**
22799  * @class Roo.form.ComboBox
22800  * @extends Roo.form.TriggerField
22801  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22802  * @constructor
22803  * Create a new ComboBox.
22804  * @param {Object} config Configuration options
22805  */
22806 Roo.form.ComboBox = function(config){
22807     Roo.form.ComboBox.superclass.constructor.call(this, config);
22808     this.addEvents({
22809         /**
22810          * @event expand
22811          * Fires when the dropdown list is expanded
22812              * @param {Roo.form.ComboBox} combo This combo box
22813              */
22814         'expand' : true,
22815         /**
22816          * @event collapse
22817          * Fires when the dropdown list is collapsed
22818              * @param {Roo.form.ComboBox} combo This combo box
22819              */
22820         'collapse' : true,
22821         /**
22822          * @event beforeselect
22823          * Fires before a list item is selected. Return false to cancel the selection.
22824              * @param {Roo.form.ComboBox} combo This combo box
22825              * @param {Roo.data.Record} record The data record returned from the underlying store
22826              * @param {Number} index The index of the selected item in the dropdown list
22827              */
22828         'beforeselect' : true,
22829         /**
22830          * @event select
22831          * Fires when a list item is selected
22832              * @param {Roo.form.ComboBox} combo This combo box
22833              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22834              * @param {Number} index The index of the selected item in the dropdown list
22835              */
22836         'select' : true,
22837         /**
22838          * @event beforequery
22839          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22840          * The event object passed has these properties:
22841              * @param {Roo.form.ComboBox} combo This combo box
22842              * @param {String} query The query
22843              * @param {Boolean} forceAll true to force "all" query
22844              * @param {Boolean} cancel true to cancel the query
22845              * @param {Object} e The query event object
22846              */
22847         'beforequery': true,
22848          /**
22849          * @event add
22850          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22851              * @param {Roo.form.ComboBox} combo This combo box
22852              */
22853         'add' : true,
22854         /**
22855          * @event edit
22856          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22857              * @param {Roo.form.ComboBox} combo This combo box
22858              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22859              */
22860         'edit' : true
22861         
22862         
22863     });
22864     if(this.transform){
22865         this.allowDomMove = false;
22866         var s = Roo.getDom(this.transform);
22867         if(!this.hiddenName){
22868             this.hiddenName = s.name;
22869         }
22870         if(!this.store){
22871             this.mode = 'local';
22872             var d = [], opts = s.options;
22873             for(var i = 0, len = opts.length;i < len; i++){
22874                 var o = opts[i];
22875                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22876                 if(o.selected) {
22877                     this.value = value;
22878                 }
22879                 d.push([value, o.text]);
22880             }
22881             this.store = new Roo.data.SimpleStore({
22882                 'id': 0,
22883                 fields: ['value', 'text'],
22884                 data : d
22885             });
22886             this.valueField = 'value';
22887             this.displayField = 'text';
22888         }
22889         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22890         if(!this.lazyRender){
22891             this.target = true;
22892             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22893             s.parentNode.removeChild(s); // remove it
22894             this.render(this.el.parentNode);
22895         }else{
22896             s.parentNode.removeChild(s); // remove it
22897         }
22898
22899     }
22900     if (this.store) {
22901         this.store = Roo.factory(this.store, Roo.data);
22902     }
22903     
22904     this.selectedIndex = -1;
22905     if(this.mode == 'local'){
22906         if(config.queryDelay === undefined){
22907             this.queryDelay = 10;
22908         }
22909         if(config.minChars === undefined){
22910             this.minChars = 0;
22911         }
22912     }
22913 };
22914
22915 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22916     /**
22917      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22918      */
22919     /**
22920      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22921      * rendering into an Roo.Editor, defaults to false)
22922      */
22923     /**
22924      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22925      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22926      */
22927     /**
22928      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22929      */
22930     /**
22931      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22932      * the dropdown list (defaults to undefined, with no header element)
22933      */
22934
22935      /**
22936      * @cfg {String/Roo.Template} tpl The template to use to render the output
22937      */
22938      
22939     // private
22940     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22941     /**
22942      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22943      */
22944     listWidth: undefined,
22945     /**
22946      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22947      * mode = 'remote' or 'text' if mode = 'local')
22948      */
22949     displayField: undefined,
22950     /**
22951      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22952      * mode = 'remote' or 'value' if mode = 'local'). 
22953      * Note: use of a valueField requires the user make a selection
22954      * in order for a value to be mapped.
22955      */
22956     valueField: undefined,
22957     
22958     
22959     /**
22960      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22961      * field's data value (defaults to the underlying DOM element's name)
22962      */
22963     hiddenName: undefined,
22964     /**
22965      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22966      */
22967     listClass: '',
22968     /**
22969      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22970      */
22971     selectedClass: 'x-combo-selected',
22972     /**
22973      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22974      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22975      * which displays a downward arrow icon).
22976      */
22977     triggerClass : 'x-form-arrow-trigger',
22978     /**
22979      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22980      */
22981     shadow:'sides',
22982     /**
22983      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22984      * anchor positions (defaults to 'tl-bl')
22985      */
22986     listAlign: 'tl-bl?',
22987     /**
22988      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22989      */
22990     maxHeight: 300,
22991     /**
22992      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22993      * query specified by the allQuery config option (defaults to 'query')
22994      */
22995     triggerAction: 'query',
22996     /**
22997      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22998      * (defaults to 4, does not apply if editable = false)
22999      */
23000     minChars : 4,
23001     /**
23002      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23003      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23004      */
23005     typeAhead: false,
23006     /**
23007      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23008      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23009      */
23010     queryDelay: 500,
23011     /**
23012      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23013      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23014      */
23015     pageSize: 0,
23016     /**
23017      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23018      * when editable = true (defaults to false)
23019      */
23020     selectOnFocus:false,
23021     /**
23022      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23023      */
23024     queryParam: 'query',
23025     /**
23026      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23027      * when mode = 'remote' (defaults to 'Loading...')
23028      */
23029     loadingText: 'Loading...',
23030     /**
23031      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23032      */
23033     resizable: false,
23034     /**
23035      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23036      */
23037     handleHeight : 8,
23038     /**
23039      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23040      * traditional select (defaults to true)
23041      */
23042     editable: true,
23043     /**
23044      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23045      */
23046     allQuery: '',
23047     /**
23048      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23049      */
23050     mode: 'remote',
23051     /**
23052      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23053      * listWidth has a higher value)
23054      */
23055     minListWidth : 70,
23056     /**
23057      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23058      * allow the user to set arbitrary text into the field (defaults to false)
23059      */
23060     forceSelection:false,
23061     /**
23062      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23063      * if typeAhead = true (defaults to 250)
23064      */
23065     typeAheadDelay : 250,
23066     /**
23067      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23068      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23069      */
23070     valueNotFoundText : undefined,
23071     /**
23072      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23073      */
23074     blockFocus : false,
23075     
23076     /**
23077      * @cfg {Boolean} disableClear Disable showing of clear button.
23078      */
23079     disableClear : false,
23080     /**
23081      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23082      */
23083     alwaysQuery : false,
23084     
23085     //private
23086     addicon : false,
23087     editicon: false,
23088     
23089     // element that contains real text value.. (when hidden is used..)
23090      
23091     // private
23092     onRender : function(ct, position){
23093         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23094         if(this.hiddenName){
23095             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23096                     'before', true);
23097             this.hiddenField.value =
23098                 this.hiddenValue !== undefined ? this.hiddenValue :
23099                 this.value !== undefined ? this.value : '';
23100
23101             // prevent input submission
23102             this.el.dom.removeAttribute('name');
23103              
23104              
23105         }
23106         if(Roo.isGecko){
23107             this.el.dom.setAttribute('autocomplete', 'off');
23108         }
23109
23110         var cls = 'x-combo-list';
23111
23112         this.list = new Roo.Layer({
23113             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23114         });
23115
23116         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23117         this.list.setWidth(lw);
23118         this.list.swallowEvent('mousewheel');
23119         this.assetHeight = 0;
23120
23121         if(this.title){
23122             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23123             this.assetHeight += this.header.getHeight();
23124         }
23125
23126         this.innerList = this.list.createChild({cls:cls+'-inner'});
23127         this.innerList.on('mouseover', this.onViewOver, this);
23128         this.innerList.on('mousemove', this.onViewMove, this);
23129         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23130         
23131         if(this.allowBlank && !this.pageSize && !this.disableClear){
23132             this.footer = this.list.createChild({cls:cls+'-ft'});
23133             this.pageTb = new Roo.Toolbar(this.footer);
23134            
23135         }
23136         if(this.pageSize){
23137             this.footer = this.list.createChild({cls:cls+'-ft'});
23138             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23139                     {pageSize: this.pageSize});
23140             
23141         }
23142         
23143         if (this.pageTb && this.allowBlank && !this.disableClear) {
23144             var _this = this;
23145             this.pageTb.add(new Roo.Toolbar.Fill(), {
23146                 cls: 'x-btn-icon x-btn-clear',
23147                 text: '&#160;',
23148                 handler: function()
23149                 {
23150                     _this.collapse();
23151                     _this.clearValue();
23152                     _this.onSelect(false, -1);
23153                 }
23154             });
23155         }
23156         if (this.footer) {
23157             this.assetHeight += this.footer.getHeight();
23158         }
23159         
23160
23161         if(!this.tpl){
23162             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23163         }
23164
23165         this.view = new Roo.View(this.innerList, this.tpl, {
23166             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23167         });
23168
23169         this.view.on('click', this.onViewClick, this);
23170
23171         this.store.on('beforeload', this.onBeforeLoad, this);
23172         this.store.on('load', this.onLoad, this);
23173         this.store.on('loadexception', this.onLoadException, this);
23174
23175         if(this.resizable){
23176             this.resizer = new Roo.Resizable(this.list,  {
23177                pinned:true, handles:'se'
23178             });
23179             this.resizer.on('resize', function(r, w, h){
23180                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23181                 this.listWidth = w;
23182                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23183                 this.restrictHeight();
23184             }, this);
23185             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23186         }
23187         if(!this.editable){
23188             this.editable = true;
23189             this.setEditable(false);
23190         }  
23191         
23192         
23193         if (typeof(this.events.add.listeners) != 'undefined') {
23194             
23195             this.addicon = this.wrap.createChild(
23196                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23197        
23198             this.addicon.on('click', function(e) {
23199                 this.fireEvent('add', this);
23200             }, this);
23201         }
23202         if (typeof(this.events.edit.listeners) != 'undefined') {
23203             
23204             this.editicon = this.wrap.createChild(
23205                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23206             if (this.addicon) {
23207                 this.editicon.setStyle('margin-left', '40px');
23208             }
23209             this.editicon.on('click', function(e) {
23210                 
23211                 // we fire even  if inothing is selected..
23212                 this.fireEvent('edit', this, this.lastData );
23213                 
23214             }, this);
23215         }
23216         
23217         
23218         
23219     },
23220
23221     // private
23222     initEvents : function(){
23223         Roo.form.ComboBox.superclass.initEvents.call(this);
23224
23225         this.keyNav = new Roo.KeyNav(this.el, {
23226             "up" : function(e){
23227                 this.inKeyMode = true;
23228                 this.selectPrev();
23229             },
23230
23231             "down" : function(e){
23232                 if(!this.isExpanded()){
23233                     this.onTriggerClick();
23234                 }else{
23235                     this.inKeyMode = true;
23236                     this.selectNext();
23237                 }
23238             },
23239
23240             "enter" : function(e){
23241                 this.onViewClick();
23242                 //return true;
23243             },
23244
23245             "esc" : function(e){
23246                 this.collapse();
23247             },
23248
23249             "tab" : function(e){
23250                 this.onViewClick(false);
23251                 this.fireEvent("specialkey", this, e);
23252                 return true;
23253             },
23254
23255             scope : this,
23256
23257             doRelay : function(foo, bar, hname){
23258                 if(hname == 'down' || this.scope.isExpanded()){
23259                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23260                 }
23261                 return true;
23262             },
23263
23264             forceKeyDown: true
23265         });
23266         this.queryDelay = Math.max(this.queryDelay || 10,
23267                 this.mode == 'local' ? 10 : 250);
23268         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23269         if(this.typeAhead){
23270             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23271         }
23272         if(this.editable !== false){
23273             this.el.on("keyup", this.onKeyUp, this);
23274         }
23275         if(this.forceSelection){
23276             this.on('blur', this.doForce, this);
23277         }
23278     },
23279
23280     onDestroy : function(){
23281         if(this.view){
23282             this.view.setStore(null);
23283             this.view.el.removeAllListeners();
23284             this.view.el.remove();
23285             this.view.purgeListeners();
23286         }
23287         if(this.list){
23288             this.list.destroy();
23289         }
23290         if(this.store){
23291             this.store.un('beforeload', this.onBeforeLoad, this);
23292             this.store.un('load', this.onLoad, this);
23293             this.store.un('loadexception', this.onLoadException, this);
23294         }
23295         Roo.form.ComboBox.superclass.onDestroy.call(this);
23296     },
23297
23298     // private
23299     fireKey : function(e){
23300         if(e.isNavKeyPress() && !this.list.isVisible()){
23301             this.fireEvent("specialkey", this, e);
23302         }
23303     },
23304
23305     // private
23306     onResize: function(w, h){
23307         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23308         
23309         if(typeof w != 'number'){
23310             // we do not handle it!?!?
23311             return;
23312         }
23313         var tw = this.trigger.getWidth();
23314         tw += this.addicon ? this.addicon.getWidth() : 0;
23315         tw += this.editicon ? this.editicon.getWidth() : 0;
23316         var x = w - tw;
23317         this.el.setWidth( this.adjustWidth('input', x));
23318             
23319         this.trigger.setStyle('left', x+'px');
23320         
23321         if(this.list && this.listWidth === undefined){
23322             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23323             this.list.setWidth(lw);
23324             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23325         }
23326         
23327     
23328         
23329     },
23330
23331     /**
23332      * Allow or prevent the user from directly editing the field text.  If false is passed,
23333      * the user will only be able to select from the items defined in the dropdown list.  This method
23334      * is the runtime equivalent of setting the 'editable' config option at config time.
23335      * @param {Boolean} value True to allow the user to directly edit the field text
23336      */
23337     setEditable : function(value){
23338         if(value == this.editable){
23339             return;
23340         }
23341         this.editable = value;
23342         if(!value){
23343             this.el.dom.setAttribute('readOnly', true);
23344             this.el.on('mousedown', this.onTriggerClick,  this);
23345             this.el.addClass('x-combo-noedit');
23346         }else{
23347             this.el.dom.setAttribute('readOnly', false);
23348             this.el.un('mousedown', this.onTriggerClick,  this);
23349             this.el.removeClass('x-combo-noedit');
23350         }
23351     },
23352
23353     // private
23354     onBeforeLoad : function(){
23355         if(!this.hasFocus){
23356             return;
23357         }
23358         this.innerList.update(this.loadingText ?
23359                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23360         this.restrictHeight();
23361         this.selectedIndex = -1;
23362     },
23363
23364     // private
23365     onLoad : function(){
23366         if(!this.hasFocus){
23367             return;
23368         }
23369         if(this.store.getCount() > 0){
23370             this.expand();
23371             this.restrictHeight();
23372             if(this.lastQuery == this.allQuery){
23373                 if(this.editable){
23374                     this.el.dom.select();
23375                 }
23376                 if(!this.selectByValue(this.value, true)){
23377                     this.select(0, true);
23378                 }
23379             }else{
23380                 this.selectNext();
23381                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23382                     this.taTask.delay(this.typeAheadDelay);
23383                 }
23384             }
23385         }else{
23386             this.onEmptyResults();
23387         }
23388         //this.el.focus();
23389     },
23390     // private
23391     onLoadException : function()
23392     {
23393         this.collapse();
23394         Roo.log(this.store.reader.jsonData);
23395         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23396             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23397         }
23398         
23399         
23400     },
23401     // private
23402     onTypeAhead : function(){
23403         if(this.store.getCount() > 0){
23404             var r = this.store.getAt(0);
23405             var newValue = r.data[this.displayField];
23406             var len = newValue.length;
23407             var selStart = this.getRawValue().length;
23408             if(selStart != len){
23409                 this.setRawValue(newValue);
23410                 this.selectText(selStart, newValue.length);
23411             }
23412         }
23413     },
23414
23415     // private
23416     onSelect : function(record, index){
23417         if(this.fireEvent('beforeselect', this, record, index) !== false){
23418             this.setFromData(index > -1 ? record.data : false);
23419             this.collapse();
23420             this.fireEvent('select', this, record, index);
23421         }
23422     },
23423
23424     /**
23425      * Returns the currently selected field value or empty string if no value is set.
23426      * @return {String} value The selected value
23427      */
23428     getValue : function(){
23429         if(this.valueField){
23430             return typeof this.value != 'undefined' ? this.value : '';
23431         }
23432         return Roo.form.ComboBox.superclass.getValue.call(this);
23433     },
23434
23435     /**
23436      * Clears any text/value currently set in the field
23437      */
23438     clearValue : function(){
23439         if(this.hiddenField){
23440             this.hiddenField.value = '';
23441         }
23442         this.value = '';
23443         this.setRawValue('');
23444         this.lastSelectionText = '';
23445         
23446     },
23447
23448     /**
23449      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23450      * will be displayed in the field.  If the value does not match the data value of an existing item,
23451      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23452      * Otherwise the field will be blank (although the value will still be set).
23453      * @param {String} value The value to match
23454      */
23455     setValue : function(v){
23456         var text = v;
23457         if(this.valueField){
23458             var r = this.findRecord(this.valueField, v);
23459             if(r){
23460                 text = r.data[this.displayField];
23461             }else if(this.valueNotFoundText !== undefined){
23462                 text = this.valueNotFoundText;
23463             }
23464         }
23465         this.lastSelectionText = text;
23466         if(this.hiddenField){
23467             this.hiddenField.value = v;
23468         }
23469         Roo.form.ComboBox.superclass.setValue.call(this, text);
23470         this.value = v;
23471     },
23472     /**
23473      * @property {Object} the last set data for the element
23474      */
23475     
23476     lastData : false,
23477     /**
23478      * Sets the value of the field based on a object which is related to the record format for the store.
23479      * @param {Object} value the value to set as. or false on reset?
23480      */
23481     setFromData : function(o){
23482         var dv = ''; // display value
23483         var vv = ''; // value value..
23484         this.lastData = o;
23485         if (this.displayField) {
23486             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23487         } else {
23488             // this is an error condition!!!
23489             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23490         }
23491         
23492         if(this.valueField){
23493             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23494         }
23495         if(this.hiddenField){
23496             this.hiddenField.value = vv;
23497             
23498             this.lastSelectionText = dv;
23499             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23500             this.value = vv;
23501             return;
23502         }
23503         // no hidden field.. - we store the value in 'value', but still display
23504         // display field!!!!
23505         this.lastSelectionText = dv;
23506         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23507         this.value = vv;
23508         
23509         
23510     },
23511     // private
23512     reset : function(){
23513         // overridden so that last data is reset..
23514         this.setValue(this.resetValue);
23515         this.clearInvalid();
23516         this.lastData = false;
23517         if (this.view) {
23518             this.view.clearSelections();
23519         }
23520     },
23521     // private
23522     findRecord : function(prop, value){
23523         var record;
23524         if(this.store.getCount() > 0){
23525             this.store.each(function(r){
23526                 if(r.data[prop] == value){
23527                     record = r;
23528                     return false;
23529                 }
23530                 return true;
23531             });
23532         }
23533         return record;
23534     },
23535     
23536     getName: function()
23537     {
23538         // returns hidden if it's set..
23539         if (!this.rendered) {return ''};
23540         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23541         
23542     },
23543     // private
23544     onViewMove : function(e, t){
23545         this.inKeyMode = false;
23546     },
23547
23548     // private
23549     onViewOver : function(e, t){
23550         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23551             return;
23552         }
23553         var item = this.view.findItemFromChild(t);
23554         if(item){
23555             var index = this.view.indexOf(item);
23556             this.select(index, false);
23557         }
23558     },
23559
23560     // private
23561     onViewClick : function(doFocus)
23562     {
23563         var index = this.view.getSelectedIndexes()[0];
23564         var r = this.store.getAt(index);
23565         if(r){
23566             this.onSelect(r, index);
23567         }
23568         if(doFocus !== false && !this.blockFocus){
23569             this.el.focus();
23570         }
23571     },
23572
23573     // private
23574     restrictHeight : function(){
23575         this.innerList.dom.style.height = '';
23576         var inner = this.innerList.dom;
23577         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23578         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23579         this.list.beginUpdate();
23580         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23581         this.list.alignTo(this.el, this.listAlign);
23582         this.list.endUpdate();
23583     },
23584
23585     // private
23586     onEmptyResults : function(){
23587         this.collapse();
23588     },
23589
23590     /**
23591      * Returns true if the dropdown list is expanded, else false.
23592      */
23593     isExpanded : function(){
23594         return this.list.isVisible();
23595     },
23596
23597     /**
23598      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23599      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23600      * @param {String} value The data value of the item to select
23601      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23602      * selected item if it is not currently in view (defaults to true)
23603      * @return {Boolean} True if the value matched an item in the list, else false
23604      */
23605     selectByValue : function(v, scrollIntoView){
23606         if(v !== undefined && v !== null){
23607             var r = this.findRecord(this.valueField || this.displayField, v);
23608             if(r){
23609                 this.select(this.store.indexOf(r), scrollIntoView);
23610                 return true;
23611             }
23612         }
23613         return false;
23614     },
23615
23616     /**
23617      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23618      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23619      * @param {Number} index The zero-based index of the list item to select
23620      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23621      * selected item if it is not currently in view (defaults to true)
23622      */
23623     select : function(index, scrollIntoView){
23624         this.selectedIndex = index;
23625         this.view.select(index);
23626         if(scrollIntoView !== false){
23627             var el = this.view.getNode(index);
23628             if(el){
23629                 this.innerList.scrollChildIntoView(el, false);
23630             }
23631         }
23632     },
23633
23634     // private
23635     selectNext : function(){
23636         var ct = this.store.getCount();
23637         if(ct > 0){
23638             if(this.selectedIndex == -1){
23639                 this.select(0);
23640             }else if(this.selectedIndex < ct-1){
23641                 this.select(this.selectedIndex+1);
23642             }
23643         }
23644     },
23645
23646     // private
23647     selectPrev : function(){
23648         var ct = this.store.getCount();
23649         if(ct > 0){
23650             if(this.selectedIndex == -1){
23651                 this.select(0);
23652             }else if(this.selectedIndex != 0){
23653                 this.select(this.selectedIndex-1);
23654             }
23655         }
23656     },
23657
23658     // private
23659     onKeyUp : function(e){
23660         if(this.editable !== false && !e.isSpecialKey()){
23661             this.lastKey = e.getKey();
23662             this.dqTask.delay(this.queryDelay);
23663         }
23664     },
23665
23666     // private
23667     validateBlur : function(){
23668         return !this.list || !this.list.isVisible();   
23669     },
23670
23671     // private
23672     initQuery : function(){
23673         this.doQuery(this.getRawValue());
23674     },
23675
23676     // private
23677     doForce : function(){
23678         if(this.el.dom.value.length > 0){
23679             this.el.dom.value =
23680                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23681              
23682         }
23683     },
23684
23685     /**
23686      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23687      * query allowing the query action to be canceled if needed.
23688      * @param {String} query The SQL query to execute
23689      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23690      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23691      * saved in the current store (defaults to false)
23692      */
23693     doQuery : function(q, forceAll){
23694         if(q === undefined || q === null){
23695             q = '';
23696         }
23697         var qe = {
23698             query: q,
23699             forceAll: forceAll,
23700             combo: this,
23701             cancel:false
23702         };
23703         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23704             return false;
23705         }
23706         q = qe.query;
23707         forceAll = qe.forceAll;
23708         if(forceAll === true || (q.length >= this.minChars)){
23709             if(this.lastQuery != q || this.alwaysQuery){
23710                 this.lastQuery = q;
23711                 if(this.mode == 'local'){
23712                     this.selectedIndex = -1;
23713                     if(forceAll){
23714                         this.store.clearFilter();
23715                     }else{
23716                         this.store.filter(this.displayField, q);
23717                     }
23718                     this.onLoad();
23719                 }else{
23720                     this.store.baseParams[this.queryParam] = q;
23721                     this.store.load({
23722                         params: this.getParams(q)
23723                     });
23724                     this.expand();
23725                 }
23726             }else{
23727                 this.selectedIndex = -1;
23728                 this.onLoad();   
23729             }
23730         }
23731     },
23732
23733     // private
23734     getParams : function(q){
23735         var p = {};
23736         //p[this.queryParam] = q;
23737         if(this.pageSize){
23738             p.start = 0;
23739             p.limit = this.pageSize;
23740         }
23741         return p;
23742     },
23743
23744     /**
23745      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23746      */
23747     collapse : function(){
23748         if(!this.isExpanded()){
23749             return;
23750         }
23751         this.list.hide();
23752         Roo.get(document).un('mousedown', this.collapseIf, this);
23753         Roo.get(document).un('mousewheel', this.collapseIf, this);
23754         if (!this.editable) {
23755             Roo.get(document).un('keydown', this.listKeyPress, this);
23756         }
23757         this.fireEvent('collapse', this);
23758     },
23759
23760     // private
23761     collapseIf : function(e){
23762         if(!e.within(this.wrap) && !e.within(this.list)){
23763             this.collapse();
23764         }
23765     },
23766
23767     /**
23768      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23769      */
23770     expand : function(){
23771         if(this.isExpanded() || !this.hasFocus){
23772             return;
23773         }
23774         this.list.alignTo(this.el, this.listAlign);
23775         this.list.show();
23776         Roo.get(document).on('mousedown', this.collapseIf, this);
23777         Roo.get(document).on('mousewheel', this.collapseIf, this);
23778         if (!this.editable) {
23779             Roo.get(document).on('keydown', this.listKeyPress, this);
23780         }
23781         
23782         this.fireEvent('expand', this);
23783     },
23784
23785     // private
23786     // Implements the default empty TriggerField.onTriggerClick function
23787     onTriggerClick : function(){
23788         if(this.disabled){
23789             return;
23790         }
23791         if(this.isExpanded()){
23792             this.collapse();
23793             if (!this.blockFocus) {
23794                 this.el.focus();
23795             }
23796             
23797         }else {
23798             this.hasFocus = true;
23799             if(this.triggerAction == 'all') {
23800                 this.doQuery(this.allQuery, true);
23801             } else {
23802                 this.doQuery(this.getRawValue());
23803             }
23804             if (!this.blockFocus) {
23805                 this.el.focus();
23806             }
23807         }
23808     },
23809     listKeyPress : function(e)
23810     {
23811         //Roo.log('listkeypress');
23812         // scroll to first matching element based on key pres..
23813         if (e.isSpecialKey()) {
23814             return false;
23815         }
23816         var k = String.fromCharCode(e.getKey()).toUpperCase();
23817         //Roo.log(k);
23818         var match  = false;
23819         var csel = this.view.getSelectedNodes();
23820         var cselitem = false;
23821         if (csel.length) {
23822             var ix = this.view.indexOf(csel[0]);
23823             cselitem  = this.store.getAt(ix);
23824             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23825                 cselitem = false;
23826             }
23827             
23828         }
23829         
23830         this.store.each(function(v) { 
23831             if (cselitem) {
23832                 // start at existing selection.
23833                 if (cselitem.id == v.id) {
23834                     cselitem = false;
23835                 }
23836                 return;
23837             }
23838                 
23839             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23840                 match = this.store.indexOf(v);
23841                 return false;
23842             }
23843         }, this);
23844         
23845         if (match === false) {
23846             return true; // no more action?
23847         }
23848         // scroll to?
23849         this.view.select(match);
23850         var sn = Roo.get(this.view.getSelectedNodes()[0])
23851         sn.scrollIntoView(sn.dom.parentNode, false);
23852     }
23853
23854     /** 
23855     * @cfg {Boolean} grow 
23856     * @hide 
23857     */
23858     /** 
23859     * @cfg {Number} growMin 
23860     * @hide 
23861     */
23862     /** 
23863     * @cfg {Number} growMax 
23864     * @hide 
23865     */
23866     /**
23867      * @hide
23868      * @method autoSize
23869      */
23870 });/*
23871  * Copyright(c) 2010-2012, Roo J Solutions Limited
23872  *
23873  * Licence LGPL
23874  *
23875  */
23876
23877 /**
23878  * @class Roo.form.ComboBoxArray
23879  * @extends Roo.form.TextField
23880  * A facebook style adder... for lists of email / people / countries  etc...
23881  * pick multiple items from a combo box, and shows each one.
23882  *
23883  *  Fred [x]  Brian [x]  [Pick another |v]
23884  *
23885  *
23886  *  For this to work: it needs various extra information
23887  *    - normal combo problay has
23888  *      name, hiddenName
23889  *    + displayField, valueField
23890  *
23891  *    For our purpose...
23892  *
23893  *
23894  *   If we change from 'extends' to wrapping...
23895  *   
23896  *  
23897  *
23898  
23899  
23900  * @constructor
23901  * Create a new ComboBoxArray.
23902  * @param {Object} config Configuration options
23903  */
23904  
23905
23906 Roo.form.ComboBoxArray = function(config)
23907 {
23908     this.addEvents({
23909         /**
23910          * @event beforeremove
23911          * Fires before remove the value from the list
23912              * @param {Roo.form.ComboBoxArray} _self This combo box array
23913              * @param {Roo.form.ComboBoxArray.Item} item removed item
23914              */
23915         'beforeremove' : true,
23916         /**
23917          * @event remove
23918          * Fires when remove the value from the list
23919              * @param {Roo.form.ComboBoxArray} _self This combo box array
23920              * @param {Roo.form.ComboBoxArray.Item} item removed item
23921              */
23922         'remove' : true
23923         
23924         
23925     });
23926     
23927     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23928     
23929     this.items = new Roo.util.MixedCollection(false);
23930     
23931     // construct the child combo...
23932     
23933     
23934     
23935     
23936    
23937     
23938 }
23939
23940  
23941 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23942
23943     /**
23944      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23945      */
23946     
23947     lastData : false,
23948     
23949     // behavies liek a hiddne field
23950     inputType:      'hidden',
23951     /**
23952      * @cfg {Number} width The width of the box that displays the selected element
23953      */ 
23954     width:          300,
23955
23956     
23957     
23958     /**
23959      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
23960      */
23961     name : false,
23962     /**
23963      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
23964      */
23965     hiddenName : false,
23966     
23967     
23968     // private the array of items that are displayed..
23969     items  : false,
23970     // private - the hidden field el.
23971     hiddenEl : false,
23972     // private - the filed el..
23973     el : false,
23974     
23975     //validateValue : function() { return true; }, // all values are ok!
23976     //onAddClick: function() { },
23977     
23978     onRender : function(ct, position) 
23979     {
23980         
23981         // create the standard hidden element
23982         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
23983         
23984         
23985         // give fake names to child combo;
23986         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
23987         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
23988         
23989         this.combo = Roo.factory(this.combo, Roo.form);
23990         this.combo.onRender(ct, position);
23991         if (typeof(this.combo.width) != 'undefined') {
23992             this.combo.onResize(this.combo.width,0);
23993         }
23994         
23995         this.combo.initEvents();
23996         
23997         // assigned so form know we need to do this..
23998         this.store          = this.combo.store;
23999         this.valueField     = this.combo.valueField;
24000         this.displayField   = this.combo.displayField ;
24001         
24002         
24003         this.combo.wrap.addClass('x-cbarray-grp');
24004         
24005         var cbwrap = this.combo.wrap.createChild(
24006             {tag: 'div', cls: 'x-cbarray-cb'},
24007             this.combo.el.dom
24008         );
24009         
24010              
24011         this.hiddenEl = this.combo.wrap.createChild({
24012             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24013         });
24014         this.el = this.combo.wrap.createChild({
24015             tag: 'input',  type:'hidden' , name: this.name, value : ''
24016         });
24017          //   this.el.dom.removeAttribute("name");
24018         
24019         
24020         this.outerWrap = this.combo.wrap;
24021         this.wrap = cbwrap;
24022         
24023         this.outerWrap.setWidth(this.width);
24024         this.outerWrap.dom.removeChild(this.el.dom);
24025         
24026         this.wrap.dom.appendChild(this.el.dom);
24027         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24028         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24029         
24030         this.combo.trigger.setStyle('position','relative');
24031         this.combo.trigger.setStyle('left', '0px');
24032         this.combo.trigger.setStyle('top', '2px');
24033         
24034         this.combo.el.setStyle('vertical-align', 'text-bottom');
24035         
24036         //this.trigger.setStyle('vertical-align', 'top');
24037         
24038         // this should use the code from combo really... on('add' ....)
24039         if (this.adder) {
24040             
24041         
24042             this.adder = this.outerWrap.createChild(
24043                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24044             var _t = this;
24045             this.adder.on('click', function(e) {
24046                 _t.fireEvent('adderclick', this, e);
24047             }, _t);
24048         }
24049         //var _t = this;
24050         //this.adder.on('click', this.onAddClick, _t);
24051         
24052         
24053         this.combo.on('select', function(cb, rec, ix) {
24054             this.addItem(rec.data);
24055             
24056             cb.setValue('');
24057             cb.el.dom.value = '';
24058             //cb.lastData = rec.data;
24059             // add to list
24060             
24061         }, this);
24062         
24063         
24064     },
24065     
24066     
24067     getName: function()
24068     {
24069         // returns hidden if it's set..
24070         if (!this.rendered) {return ''};
24071         return  this.hiddenName ? this.hiddenName : this.name;
24072         
24073     },
24074     
24075     
24076     onResize: function(w, h){
24077         
24078         return;
24079         // not sure if this is needed..
24080         //this.combo.onResize(w,h);
24081         
24082         if(typeof w != 'number'){
24083             // we do not handle it!?!?
24084             return;
24085         }
24086         var tw = this.combo.trigger.getWidth();
24087         tw += this.addicon ? this.addicon.getWidth() : 0;
24088         tw += this.editicon ? this.editicon.getWidth() : 0;
24089         var x = w - tw;
24090         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24091             
24092         this.combo.trigger.setStyle('left', '0px');
24093         
24094         if(this.list && this.listWidth === undefined){
24095             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24096             this.list.setWidth(lw);
24097             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24098         }
24099         
24100     
24101         
24102     },
24103     
24104     addItem: function(rec)
24105     {
24106         var valueField = this.combo.valueField;
24107         var displayField = this.combo.displayField;
24108         if (this.items.indexOfKey(rec[valueField]) > -1) {
24109             //console.log("GOT " + rec.data.id);
24110             return;
24111         }
24112         
24113         var x = new Roo.form.ComboBoxArray.Item({
24114             //id : rec[this.idField],
24115             data : rec,
24116             displayField : displayField ,
24117             tipField : displayField ,
24118             cb : this
24119         });
24120         // use the 
24121         this.items.add(rec[valueField],x);
24122         // add it before the element..
24123         this.updateHiddenEl();
24124         x.render(this.outerWrap, this.wrap.dom);
24125         // add the image handler..
24126     },
24127     
24128     updateHiddenEl : function()
24129     {
24130         this.validate();
24131         if (!this.hiddenEl) {
24132             return;
24133         }
24134         var ar = [];
24135         var idField = this.combo.valueField;
24136         
24137         this.items.each(function(f) {
24138             ar.push(f.data[idField]);
24139            
24140         });
24141         this.hiddenEl.dom.value = ar.join(',');
24142         this.validate();
24143     },
24144     
24145     reset : function()
24146     {
24147         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24148         this.items.each(function(f) {
24149            f.remove(); 
24150         });
24151         this.el.dom.value = '';
24152         if (this.hiddenEl) {
24153             this.hiddenEl.dom.value = '';
24154         }
24155         
24156     },
24157     getValue: function()
24158     {
24159         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24160     },
24161     setValue: function(v) // not a valid action - must use addItems..
24162     {
24163          
24164         this.reset();
24165         
24166         
24167         
24168         if (this.store.isLocal && (typeof(v) == 'string')) {
24169             // then we can use the store to find the values..
24170             // comma seperated at present.. this needs to allow JSON based encoding..
24171             this.hiddenEl.value  = v;
24172             var v_ar = [];
24173             Roo.each(v.split(','), function(k) {
24174                 Roo.log("CHECK " + this.valueField + ',' + k);
24175                 var li = this.store.query(this.valueField, k);
24176                 if (!li.length) {
24177                     return;
24178                 }
24179                 var add = {};
24180                 add[this.valueField] = k;
24181                 add[this.displayField] = li.item(0).data[this.displayField];
24182                 
24183                 this.addItem(add);
24184             }, this) 
24185              
24186         }
24187         if (typeof(v) == 'object' ) {
24188             // then let's assume it's an array of objects..
24189             Roo.each(v, function(l) {
24190                 this.addItem(l);
24191             }, this);
24192              
24193         }
24194         
24195         
24196     },
24197     setFromData: function(v)
24198     {
24199         // this recieves an object, if setValues is called.
24200         this.reset();
24201         this.el.dom.value = v[this.displayField];
24202         this.hiddenEl.dom.value = v[this.valueField];
24203         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24204             return;
24205         }
24206         var kv = v[this.valueField];
24207         var dv = v[this.displayField];
24208         kv = typeof(kv) != 'string' ? '' : kv;
24209         dv = typeof(dv) != 'string' ? '' : dv;
24210         
24211         
24212         var keys = kv.split(',');
24213         var display = dv.split(',');
24214         for (var i = 0 ; i < keys.length; i++) {
24215             
24216             add = {};
24217             add[this.valueField] = keys[i];
24218             add[this.displayField] = display[i];
24219             this.addItem(add);
24220         }
24221       
24222         
24223     },
24224     
24225     /**
24226      * Validates the combox array value
24227      * @return {Boolean} True if the value is valid, else false
24228      */
24229     validate : function(){
24230         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24231             this.clearInvalid();
24232             return true;
24233         }
24234         return false;
24235     },
24236     
24237     validateValue : function(value){
24238         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24239         
24240     },
24241     
24242     /*@
24243      * overide
24244      * 
24245      */
24246     isDirty : function() {
24247         if(this.disabled) {
24248             return false;
24249         }
24250         
24251         try {
24252             var d = Roo.decode(String(this.originalValue));
24253         } catch (e) {
24254             return String(this.getValue()) !== String(this.originalValue);
24255         }
24256         
24257         var originalValue = [];
24258         
24259         for (var i = 0; i < d.length; i++){
24260             originalValue.push(d[i][this.valueField]);
24261         }
24262         
24263         return String(this.getValue()) !== String(originalValue.join(','));
24264         
24265     }
24266     
24267 });
24268
24269
24270
24271 /**
24272  * @class Roo.form.ComboBoxArray.Item
24273  * @extends Roo.BoxComponent
24274  * A selected item in the list
24275  *  Fred [x]  Brian [x]  [Pick another |v]
24276  * 
24277  * @constructor
24278  * Create a new item.
24279  * @param {Object} config Configuration options
24280  */
24281  
24282 Roo.form.ComboBoxArray.Item = function(config) {
24283     config.id = Roo.id();
24284     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24285 }
24286
24287 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24288     data : {},
24289     cb: false,
24290     displayField : false,
24291     tipField : false,
24292     
24293     
24294     defaultAutoCreate : {
24295         tag: 'div',
24296         cls: 'x-cbarray-item',
24297         cn : [ 
24298             { tag: 'div' },
24299             {
24300                 tag: 'img',
24301                 width:16,
24302                 height : 16,
24303                 src : Roo.BLANK_IMAGE_URL ,
24304                 align: 'center'
24305             }
24306         ]
24307         
24308     },
24309     
24310  
24311     onRender : function(ct, position)
24312     {
24313         Roo.form.Field.superclass.onRender.call(this, ct, position);
24314         
24315         if(!this.el){
24316             var cfg = this.getAutoCreate();
24317             this.el = ct.createChild(cfg, position);
24318         }
24319         
24320         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24321         
24322         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24323             this.cb.renderer(this.data) :
24324             String.format('{0}',this.data[this.displayField]);
24325         
24326             
24327         this.el.child('div').dom.setAttribute('qtip',
24328                         String.format('{0}',this.data[this.tipField])
24329         );
24330         
24331         this.el.child('img').on('click', this.remove, this);
24332         
24333     },
24334    
24335     remove : function()
24336     {
24337         if(this.cb.disabled){
24338             return;
24339         }
24340         
24341         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24342             this.cb.items.remove(this);
24343             this.el.child('img').un('click', this.remove, this);
24344             this.el.remove();
24345             this.cb.updateHiddenEl();
24346
24347             this.cb.fireEvent('remove', this.cb, this);
24348         }
24349         
24350     }
24351 });/*
24352  * Based on:
24353  * Ext JS Library 1.1.1
24354  * Copyright(c) 2006-2007, Ext JS, LLC.
24355  *
24356  * Originally Released Under LGPL - original licence link has changed is not relivant.
24357  *
24358  * Fork - LGPL
24359  * <script type="text/javascript">
24360  */
24361 /**
24362  * @class Roo.form.Checkbox
24363  * @extends Roo.form.Field
24364  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24365  * @constructor
24366  * Creates a new Checkbox
24367  * @param {Object} config Configuration options
24368  */
24369 Roo.form.Checkbox = function(config){
24370     Roo.form.Checkbox.superclass.constructor.call(this, config);
24371     this.addEvents({
24372         /**
24373          * @event check
24374          * Fires when the checkbox is checked or unchecked.
24375              * @param {Roo.form.Checkbox} this This checkbox
24376              * @param {Boolean} checked The new checked value
24377              */
24378         check : true
24379     });
24380 };
24381
24382 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24383     /**
24384      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24385      */
24386     focusClass : undefined,
24387     /**
24388      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24389      */
24390     fieldClass: "x-form-field",
24391     /**
24392      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24393      */
24394     checked: false,
24395     /**
24396      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24397      * {tag: "input", type: "checkbox", autocomplete: "off"})
24398      */
24399     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24400     /**
24401      * @cfg {String} boxLabel The text that appears beside the checkbox
24402      */
24403     boxLabel : "",
24404     /**
24405      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24406      */  
24407     inputValue : '1',
24408     /**
24409      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24410      */
24411      valueOff: '0', // value when not checked..
24412
24413     actionMode : 'viewEl', 
24414     //
24415     // private
24416     itemCls : 'x-menu-check-item x-form-item',
24417     groupClass : 'x-menu-group-item',
24418     inputType : 'hidden',
24419     
24420     
24421     inSetChecked: false, // check that we are not calling self...
24422     
24423     inputElement: false, // real input element?
24424     basedOn: false, // ????
24425     
24426     isFormField: true, // not sure where this is needed!!!!
24427
24428     onResize : function(){
24429         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24430         if(!this.boxLabel){
24431             this.el.alignTo(this.wrap, 'c-c');
24432         }
24433     },
24434
24435     initEvents : function(){
24436         Roo.form.Checkbox.superclass.initEvents.call(this);
24437         this.el.on("click", this.onClick,  this);
24438         this.el.on("change", this.onClick,  this);
24439     },
24440
24441
24442     getResizeEl : function(){
24443         return this.wrap;
24444     },
24445
24446     getPositionEl : function(){
24447         return this.wrap;
24448     },
24449
24450     // private
24451     onRender : function(ct, position){
24452         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24453         /*
24454         if(this.inputValue !== undefined){
24455             this.el.dom.value = this.inputValue;
24456         }
24457         */
24458         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24459         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24460         var viewEl = this.wrap.createChild({ 
24461             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24462         this.viewEl = viewEl;   
24463         this.wrap.on('click', this.onClick,  this); 
24464         
24465         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24466         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24467         
24468         
24469         
24470         if(this.boxLabel){
24471             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24472         //    viewEl.on('click', this.onClick,  this); 
24473         }
24474         //if(this.checked){
24475             this.setChecked(this.checked);
24476         //}else{
24477             //this.checked = this.el.dom;
24478         //}
24479
24480     },
24481
24482     // private
24483     initValue : Roo.emptyFn,
24484
24485     /**
24486      * Returns the checked state of the checkbox.
24487      * @return {Boolean} True if checked, else false
24488      */
24489     getValue : function(){
24490         if(this.el){
24491             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24492         }
24493         return this.valueOff;
24494         
24495     },
24496
24497         // private
24498     onClick : function(){ 
24499         if (this.disabled) {
24500             return;
24501         }
24502         this.setChecked(!this.checked);
24503
24504         //if(this.el.dom.checked != this.checked){
24505         //    this.setValue(this.el.dom.checked);
24506        // }
24507     },
24508
24509     /**
24510      * Sets the checked state of the checkbox.
24511      * On is always based on a string comparison between inputValue and the param.
24512      * @param {Boolean/String} value - the value to set 
24513      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24514      */
24515     setValue : function(v,suppressEvent){
24516         
24517         
24518         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24519         //if(this.el && this.el.dom){
24520         //    this.el.dom.checked = this.checked;
24521         //    this.el.dom.defaultChecked = this.checked;
24522         //}
24523         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24524         //this.fireEvent("check", this, this.checked);
24525     },
24526     // private..
24527     setChecked : function(state,suppressEvent)
24528     {
24529         if (this.inSetChecked) {
24530             this.checked = state;
24531             return;
24532         }
24533         
24534     
24535         if(this.wrap){
24536             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24537         }
24538         this.checked = state;
24539         if(suppressEvent !== true){
24540             this.fireEvent('check', this, state);
24541         }
24542         this.inSetChecked = true;
24543         this.el.dom.value = state ? this.inputValue : this.valueOff;
24544         this.inSetChecked = false;
24545         
24546     },
24547     // handle setting of hidden value by some other method!!?!?
24548     setFromHidden: function()
24549     {
24550         if(!this.el){
24551             return;
24552         }
24553         //console.log("SET FROM HIDDEN");
24554         //alert('setFrom hidden');
24555         this.setValue(this.el.dom.value);
24556     },
24557     
24558     onDestroy : function()
24559     {
24560         if(this.viewEl){
24561             Roo.get(this.viewEl).remove();
24562         }
24563          
24564         Roo.form.Checkbox.superclass.onDestroy.call(this);
24565     }
24566
24567 });/*
24568  * Based on:
24569  * Ext JS Library 1.1.1
24570  * Copyright(c) 2006-2007, Ext JS, LLC.
24571  *
24572  * Originally Released Under LGPL - original licence link has changed is not relivant.
24573  *
24574  * Fork - LGPL
24575  * <script type="text/javascript">
24576  */
24577  
24578 /**
24579  * @class Roo.form.Radio
24580  * @extends Roo.form.Checkbox
24581  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24582  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24583  * @constructor
24584  * Creates a new Radio
24585  * @param {Object} config Configuration options
24586  */
24587 Roo.form.Radio = function(){
24588     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24589 };
24590 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24591     inputType: 'radio',
24592
24593     /**
24594      * If this radio is part of a group, it will return the selected value
24595      * @return {String}
24596      */
24597     getGroupValue : function(){
24598         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24599     },
24600     
24601     
24602     onRender : function(ct, position){
24603         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24604         
24605         if(this.inputValue !== undefined){
24606             this.el.dom.value = this.inputValue;
24607         }
24608          
24609         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24610         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24611         //var viewEl = this.wrap.createChild({ 
24612         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24613         //this.viewEl = viewEl;   
24614         //this.wrap.on('click', this.onClick,  this); 
24615         
24616         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24617         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24618         
24619         
24620         
24621         if(this.boxLabel){
24622             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24623         //    viewEl.on('click', this.onClick,  this); 
24624         }
24625          if(this.checked){
24626             this.el.dom.checked =   'checked' ;
24627         }
24628          
24629     } 
24630     
24631     
24632 });//<script type="text/javascript">
24633
24634 /*
24635  * Based  Ext JS Library 1.1.1
24636  * Copyright(c) 2006-2007, Ext JS, LLC.
24637  * LGPL
24638  *
24639  */
24640  
24641 /**
24642  * @class Roo.HtmlEditorCore
24643  * @extends Roo.Component
24644  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24645  *
24646  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24647  */
24648
24649 Roo.HtmlEditorCore = function(config){
24650     
24651     
24652     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24653     
24654     
24655     this.addEvents({
24656         /**
24657          * @event initialize
24658          * Fires when the editor is fully initialized (including the iframe)
24659          * @param {Roo.HtmlEditorCore} this
24660          */
24661         initialize: true,
24662         /**
24663          * @event activate
24664          * Fires when the editor is first receives the focus. Any insertion must wait
24665          * until after this event.
24666          * @param {Roo.HtmlEditorCore} this
24667          */
24668         activate: true,
24669          /**
24670          * @event beforesync
24671          * Fires before the textarea is updated with content from the editor iframe. Return false
24672          * to cancel the sync.
24673          * @param {Roo.HtmlEditorCore} this
24674          * @param {String} html
24675          */
24676         beforesync: true,
24677          /**
24678          * @event beforepush
24679          * Fires before the iframe editor is updated with content from the textarea. Return false
24680          * to cancel the push.
24681          * @param {Roo.HtmlEditorCore} this
24682          * @param {String} html
24683          */
24684         beforepush: true,
24685          /**
24686          * @event sync
24687          * Fires when the textarea is updated with content from the editor iframe.
24688          * @param {Roo.HtmlEditorCore} this
24689          * @param {String} html
24690          */
24691         sync: true,
24692          /**
24693          * @event push
24694          * Fires when the iframe editor is updated with content from the textarea.
24695          * @param {Roo.HtmlEditorCore} this
24696          * @param {String} html
24697          */
24698         push: true,
24699         
24700         /**
24701          * @event editorevent
24702          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24703          * @param {Roo.HtmlEditorCore} this
24704          */
24705         editorevent: true
24706     });
24707     
24708     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24709     
24710     // defaults : white / black...
24711     this.applyBlacklists();
24712     
24713     
24714     
24715 };
24716
24717
24718 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24719
24720
24721      /**
24722      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24723      */
24724     
24725     owner : false,
24726     
24727      /**
24728      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24729      *                        Roo.resizable.
24730      */
24731     resizable : false,
24732      /**
24733      * @cfg {Number} height (in pixels)
24734      */   
24735     height: 300,
24736    /**
24737      * @cfg {Number} width (in pixels)
24738      */   
24739     width: 500,
24740     
24741     /**
24742      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24743      * 
24744      */
24745     stylesheets: false,
24746     
24747     // id of frame..
24748     frameId: false,
24749     
24750     // private properties
24751     validationEvent : false,
24752     deferHeight: true,
24753     initialized : false,
24754     activated : false,
24755     sourceEditMode : false,
24756     onFocus : Roo.emptyFn,
24757     iframePad:3,
24758     hideMode:'offsets',
24759     
24760     clearUp: true,
24761     
24762     // blacklist + whitelisted elements..
24763     black: false,
24764     white: false,
24765      
24766     
24767
24768     /**
24769      * Protected method that will not generally be called directly. It
24770      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24771      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24772      */
24773     getDocMarkup : function(){
24774         // body styles..
24775         var st = '';
24776         Roo.log(this.stylesheets);
24777         
24778         // inherit styels from page...?? 
24779         if (this.stylesheets === false) {
24780             
24781             Roo.get(document.head).select('style').each(function(node) {
24782                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24783             });
24784             
24785             Roo.get(document.head).select('link').each(function(node) { 
24786                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24787             });
24788             
24789         } else if (!this.stylesheets.length) {
24790                 // simple..
24791                 st = '<style type="text/css">' +
24792                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24793                    '</style>';
24794         } else {
24795             Roo.each(this.stylesheets, function(s) {
24796                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24797             });
24798             
24799         }
24800         
24801         st +=  '<style type="text/css">' +
24802             'IMG { cursor: pointer } ' +
24803         '</style>';
24804
24805         
24806         return '<html><head>' + st  +
24807             //<style type="text/css">' +
24808             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24809             //'</style>' +
24810             ' </head><body class="roo-htmleditor-body"></body></html>';
24811     },
24812
24813     // private
24814     onRender : function(ct, position)
24815     {
24816         var _t = this;
24817         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24818         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24819         
24820         
24821         this.el.dom.style.border = '0 none';
24822         this.el.dom.setAttribute('tabIndex', -1);
24823         this.el.addClass('x-hidden hide');
24824         
24825         
24826         
24827         if(Roo.isIE){ // fix IE 1px bogus margin
24828             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24829         }
24830        
24831         
24832         this.frameId = Roo.id();
24833         
24834          
24835         
24836         var iframe = this.owner.wrap.createChild({
24837             tag: 'iframe',
24838             cls: 'form-control', // bootstrap..
24839             id: this.frameId,
24840             name: this.frameId,
24841             frameBorder : 'no',
24842             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24843         }, this.el
24844         );
24845         
24846         
24847         this.iframe = iframe.dom;
24848
24849          this.assignDocWin();
24850         
24851         this.doc.designMode = 'on';
24852        
24853         this.doc.open();
24854         this.doc.write(this.getDocMarkup());
24855         this.doc.close();
24856
24857         
24858         var task = { // must defer to wait for browser to be ready
24859             run : function(){
24860                 //console.log("run task?" + this.doc.readyState);
24861                 this.assignDocWin();
24862                 if(this.doc.body || this.doc.readyState == 'complete'){
24863                     try {
24864                         this.doc.designMode="on";
24865                     } catch (e) {
24866                         return;
24867                     }
24868                     Roo.TaskMgr.stop(task);
24869                     this.initEditor.defer(10, this);
24870                 }
24871             },
24872             interval : 10,
24873             duration: 10000,
24874             scope: this
24875         };
24876         Roo.TaskMgr.start(task);
24877
24878         
24879          
24880     },
24881
24882     // private
24883     onResize : function(w, h)
24884     {
24885          Roo.log('resize: ' +w + ',' + h );
24886         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24887         if(!this.iframe){
24888             return;
24889         }
24890         if(typeof w == 'number'){
24891             
24892             this.iframe.style.width = w + 'px';
24893         }
24894         if(typeof h == 'number'){
24895             
24896             this.iframe.style.height = h + 'px';
24897             if(this.doc){
24898                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24899             }
24900         }
24901         
24902     },
24903
24904     /**
24905      * Toggles the editor between standard and source edit mode.
24906      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24907      */
24908     toggleSourceEdit : function(sourceEditMode){
24909         
24910         this.sourceEditMode = sourceEditMode === true;
24911         
24912         if(this.sourceEditMode){
24913  
24914             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24915             
24916         }else{
24917             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24918             //this.iframe.className = '';
24919             this.deferFocus();
24920         }
24921         //this.setSize(this.owner.wrap.getSize());
24922         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24923     },
24924
24925     
24926   
24927
24928     /**
24929      * Protected method that will not generally be called directly. If you need/want
24930      * custom HTML cleanup, this is the method you should override.
24931      * @param {String} html The HTML to be cleaned
24932      * return {String} The cleaned HTML
24933      */
24934     cleanHtml : function(html){
24935         html = String(html);
24936         if(html.length > 5){
24937             if(Roo.isSafari){ // strip safari nonsense
24938                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24939             }
24940         }
24941         if(html == '&nbsp;'){
24942             html = '';
24943         }
24944         return html;
24945     },
24946
24947     /**
24948      * HTML Editor -> Textarea
24949      * Protected method that will not generally be called directly. Syncs the contents
24950      * of the editor iframe with the textarea.
24951      */
24952     syncValue : function(){
24953         if(this.initialized){
24954             var bd = (this.doc.body || this.doc.documentElement);
24955             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24956             var html = bd.innerHTML;
24957             if(Roo.isSafari){
24958                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24959                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24960                 if(m && m[1]){
24961                     html = '<div style="'+m[0]+'">' + html + '</div>';
24962                 }
24963             }
24964             html = this.cleanHtml(html);
24965             // fix up the special chars.. normaly like back quotes in word...
24966             // however we do not want to do this with chinese..
24967             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24968                 var cc = b.charCodeAt();
24969                 if (
24970                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24971                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24972                     (cc >= 0xf900 && cc < 0xfb00 )
24973                 ) {
24974                         return b;
24975                 }
24976                 return "&#"+cc+";" 
24977             });
24978             if(this.owner.fireEvent('beforesync', this, html) !== false){
24979                 this.el.dom.value = html;
24980                 this.owner.fireEvent('sync', this, html);
24981             }
24982         }
24983     },
24984
24985     /**
24986      * Protected method that will not generally be called directly. Pushes the value of the textarea
24987      * into the iframe editor.
24988      */
24989     pushValue : function(){
24990         if(this.initialized){
24991             var v = this.el.dom.value.trim();
24992             
24993 //            if(v.length < 1){
24994 //                v = '&#160;';
24995 //            }
24996             
24997             if(this.owner.fireEvent('beforepush', this, v) !== false){
24998                 var d = (this.doc.body || this.doc.documentElement);
24999                 d.innerHTML = v;
25000                 this.cleanUpPaste();
25001                 this.el.dom.value = d.innerHTML;
25002                 this.owner.fireEvent('push', this, v);
25003             }
25004         }
25005     },
25006
25007     // private
25008     deferFocus : function(){
25009         this.focus.defer(10, this);
25010     },
25011
25012     // doc'ed in Field
25013     focus : function(){
25014         if(this.win && !this.sourceEditMode){
25015             this.win.focus();
25016         }else{
25017             this.el.focus();
25018         }
25019     },
25020     
25021     assignDocWin: function()
25022     {
25023         var iframe = this.iframe;
25024         
25025          if(Roo.isIE){
25026             this.doc = iframe.contentWindow.document;
25027             this.win = iframe.contentWindow;
25028         } else {
25029 //            if (!Roo.get(this.frameId)) {
25030 //                return;
25031 //            }
25032 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25033 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25034             
25035             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25036                 return;
25037             }
25038             
25039             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25040             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25041         }
25042     },
25043     
25044     // private
25045     initEditor : function(){
25046         //console.log("INIT EDITOR");
25047         this.assignDocWin();
25048         
25049         
25050         
25051         this.doc.designMode="on";
25052         this.doc.open();
25053         this.doc.write(this.getDocMarkup());
25054         this.doc.close();
25055         
25056         var dbody = (this.doc.body || this.doc.documentElement);
25057         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25058         // this copies styles from the containing element into thsi one..
25059         // not sure why we need all of this..
25060         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25061         
25062         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25063         //ss['background-attachment'] = 'fixed'; // w3c
25064         dbody.bgProperties = 'fixed'; // ie
25065         //Roo.DomHelper.applyStyles(dbody, ss);
25066         Roo.EventManager.on(this.doc, {
25067             //'mousedown': this.onEditorEvent,
25068             'mouseup': this.onEditorEvent,
25069             'dblclick': this.onEditorEvent,
25070             'click': this.onEditorEvent,
25071             'keyup': this.onEditorEvent,
25072             buffer:100,
25073             scope: this
25074         });
25075         if(Roo.isGecko){
25076             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25077         }
25078         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25079             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25080         }
25081         this.initialized = true;
25082
25083         this.owner.fireEvent('initialize', this);
25084         this.pushValue();
25085     },
25086
25087     // private
25088     onDestroy : function(){
25089         
25090         
25091         
25092         if(this.rendered){
25093             
25094             //for (var i =0; i < this.toolbars.length;i++) {
25095             //    // fixme - ask toolbars for heights?
25096             //    this.toolbars[i].onDestroy();
25097            // }
25098             
25099             //this.wrap.dom.innerHTML = '';
25100             //this.wrap.remove();
25101         }
25102     },
25103
25104     // private
25105     onFirstFocus : function(){
25106         
25107         this.assignDocWin();
25108         
25109         
25110         this.activated = true;
25111          
25112     
25113         if(Roo.isGecko){ // prevent silly gecko errors
25114             this.win.focus();
25115             var s = this.win.getSelection();
25116             if(!s.focusNode || s.focusNode.nodeType != 3){
25117                 var r = s.getRangeAt(0);
25118                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25119                 r.collapse(true);
25120                 this.deferFocus();
25121             }
25122             try{
25123                 this.execCmd('useCSS', true);
25124                 this.execCmd('styleWithCSS', false);
25125             }catch(e){}
25126         }
25127         this.owner.fireEvent('activate', this);
25128     },
25129
25130     // private
25131     adjustFont: function(btn){
25132         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25133         //if(Roo.isSafari){ // safari
25134         //    adjust *= 2;
25135        // }
25136         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25137         if(Roo.isSafari){ // safari
25138             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25139             v =  (v < 10) ? 10 : v;
25140             v =  (v > 48) ? 48 : v;
25141             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25142             
25143         }
25144         
25145         
25146         v = Math.max(1, v+adjust);
25147         
25148         this.execCmd('FontSize', v  );
25149     },
25150
25151     onEditorEvent : function(e){
25152         this.owner.fireEvent('editorevent', this, e);
25153       //  this.updateToolbar();
25154         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25155     },
25156
25157     insertTag : function(tg)
25158     {
25159         // could be a bit smarter... -> wrap the current selected tRoo..
25160         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25161             
25162             range = this.createRange(this.getSelection());
25163             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25164             wrappingNode.appendChild(range.extractContents());
25165             range.insertNode(wrappingNode);
25166
25167             return;
25168             
25169             
25170             
25171         }
25172         this.execCmd("formatblock",   tg);
25173         
25174     },
25175     
25176     insertText : function(txt)
25177     {
25178         
25179         
25180         var range = this.createRange();
25181         range.deleteContents();
25182                //alert(Sender.getAttribute('label'));
25183                
25184         range.insertNode(this.doc.createTextNode(txt));
25185     } ,
25186     
25187      
25188
25189     /**
25190      * Executes a Midas editor command on the editor document and performs necessary focus and
25191      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25192      * @param {String} cmd The Midas command
25193      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25194      */
25195     relayCmd : function(cmd, value){
25196         this.win.focus();
25197         this.execCmd(cmd, value);
25198         this.owner.fireEvent('editorevent', this);
25199         //this.updateToolbar();
25200         this.owner.deferFocus();
25201     },
25202
25203     /**
25204      * Executes a Midas editor command directly on the editor document.
25205      * For visual commands, you should use {@link #relayCmd} instead.
25206      * <b>This should only be called after the editor is initialized.</b>
25207      * @param {String} cmd The Midas command
25208      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25209      */
25210     execCmd : function(cmd, value){
25211         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25212         this.syncValue();
25213     },
25214  
25215  
25216    
25217     /**
25218      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25219      * to insert tRoo.
25220      * @param {String} text | dom node.. 
25221      */
25222     insertAtCursor : function(text)
25223     {
25224         
25225         
25226         
25227         if(!this.activated){
25228             return;
25229         }
25230         /*
25231         if(Roo.isIE){
25232             this.win.focus();
25233             var r = this.doc.selection.createRange();
25234             if(r){
25235                 r.collapse(true);
25236                 r.pasteHTML(text);
25237                 this.syncValue();
25238                 this.deferFocus();
25239             
25240             }
25241             return;
25242         }
25243         */
25244         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25245             this.win.focus();
25246             
25247             
25248             // from jquery ui (MIT licenced)
25249             var range, node;
25250             var win = this.win;
25251             
25252             if (win.getSelection && win.getSelection().getRangeAt) {
25253                 range = win.getSelection().getRangeAt(0);
25254                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25255                 range.insertNode(node);
25256             } else if (win.document.selection && win.document.selection.createRange) {
25257                 // no firefox support
25258                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25259                 win.document.selection.createRange().pasteHTML(txt);
25260             } else {
25261                 // no firefox support
25262                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25263                 this.execCmd('InsertHTML', txt);
25264             } 
25265             
25266             this.syncValue();
25267             
25268             this.deferFocus();
25269         }
25270     },
25271  // private
25272     mozKeyPress : function(e){
25273         if(e.ctrlKey){
25274             var c = e.getCharCode(), cmd;
25275           
25276             if(c > 0){
25277                 c = String.fromCharCode(c).toLowerCase();
25278                 switch(c){
25279                     case 'b':
25280                         cmd = 'bold';
25281                         break;
25282                     case 'i':
25283                         cmd = 'italic';
25284                         break;
25285                     
25286                     case 'u':
25287                         cmd = 'underline';
25288                         break;
25289                     
25290                     case 'v':
25291                         this.cleanUpPaste.defer(100, this);
25292                         return;
25293                         
25294                 }
25295                 if(cmd){
25296                     this.win.focus();
25297                     this.execCmd(cmd);
25298                     this.deferFocus();
25299                     e.preventDefault();
25300                 }
25301                 
25302             }
25303         }
25304     },
25305
25306     // private
25307     fixKeys : function(){ // load time branching for fastest keydown performance
25308         if(Roo.isIE){
25309             return function(e){
25310                 var k = e.getKey(), r;
25311                 if(k == e.TAB){
25312                     e.stopEvent();
25313                     r = this.doc.selection.createRange();
25314                     if(r){
25315                         r.collapse(true);
25316                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25317                         this.deferFocus();
25318                     }
25319                     return;
25320                 }
25321                 
25322                 if(k == e.ENTER){
25323                     r = this.doc.selection.createRange();
25324                     if(r){
25325                         var target = r.parentElement();
25326                         if(!target || target.tagName.toLowerCase() != 'li'){
25327                             e.stopEvent();
25328                             r.pasteHTML('<br />');
25329                             r.collapse(false);
25330                             r.select();
25331                         }
25332                     }
25333                 }
25334                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25335                     this.cleanUpPaste.defer(100, this);
25336                     return;
25337                 }
25338                 
25339                 
25340             };
25341         }else if(Roo.isOpera){
25342             return function(e){
25343                 var k = e.getKey();
25344                 if(k == e.TAB){
25345                     e.stopEvent();
25346                     this.win.focus();
25347                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25348                     this.deferFocus();
25349                 }
25350                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25351                     this.cleanUpPaste.defer(100, this);
25352                     return;
25353                 }
25354                 
25355             };
25356         }else if(Roo.isSafari){
25357             return function(e){
25358                 var k = e.getKey();
25359                 
25360                 if(k == e.TAB){
25361                     e.stopEvent();
25362                     this.execCmd('InsertText','\t');
25363                     this.deferFocus();
25364                     return;
25365                 }
25366                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25367                     this.cleanUpPaste.defer(100, this);
25368                     return;
25369                 }
25370                 
25371              };
25372         }
25373     }(),
25374     
25375     getAllAncestors: function()
25376     {
25377         var p = this.getSelectedNode();
25378         var a = [];
25379         if (!p) {
25380             a.push(p); // push blank onto stack..
25381             p = this.getParentElement();
25382         }
25383         
25384         
25385         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25386             a.push(p);
25387             p = p.parentNode;
25388         }
25389         a.push(this.doc.body);
25390         return a;
25391     },
25392     lastSel : false,
25393     lastSelNode : false,
25394     
25395     
25396     getSelection : function() 
25397     {
25398         this.assignDocWin();
25399         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25400     },
25401     
25402     getSelectedNode: function() 
25403     {
25404         // this may only work on Gecko!!!
25405         
25406         // should we cache this!!!!
25407         
25408         
25409         
25410          
25411         var range = this.createRange(this.getSelection()).cloneRange();
25412         
25413         if (Roo.isIE) {
25414             var parent = range.parentElement();
25415             while (true) {
25416                 var testRange = range.duplicate();
25417                 testRange.moveToElementText(parent);
25418                 if (testRange.inRange(range)) {
25419                     break;
25420                 }
25421                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25422                     break;
25423                 }
25424                 parent = parent.parentElement;
25425             }
25426             return parent;
25427         }
25428         
25429         // is ancestor a text element.
25430         var ac =  range.commonAncestorContainer;
25431         if (ac.nodeType == 3) {
25432             ac = ac.parentNode;
25433         }
25434         
25435         var ar = ac.childNodes;
25436          
25437         var nodes = [];
25438         var other_nodes = [];
25439         var has_other_nodes = false;
25440         for (var i=0;i<ar.length;i++) {
25441             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25442                 continue;
25443             }
25444             // fullly contained node.
25445             
25446             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25447                 nodes.push(ar[i]);
25448                 continue;
25449             }
25450             
25451             // probably selected..
25452             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25453                 other_nodes.push(ar[i]);
25454                 continue;
25455             }
25456             // outer..
25457             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25458                 continue;
25459             }
25460             
25461             
25462             has_other_nodes = true;
25463         }
25464         if (!nodes.length && other_nodes.length) {
25465             nodes= other_nodes;
25466         }
25467         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25468             return false;
25469         }
25470         
25471         return nodes[0];
25472     },
25473     createRange: function(sel)
25474     {
25475         // this has strange effects when using with 
25476         // top toolbar - not sure if it's a great idea.
25477         //this.editor.contentWindow.focus();
25478         if (typeof sel != "undefined") {
25479             try {
25480                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25481             } catch(e) {
25482                 return this.doc.createRange();
25483             }
25484         } else {
25485             return this.doc.createRange();
25486         }
25487     },
25488     getParentElement: function()
25489     {
25490         
25491         this.assignDocWin();
25492         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25493         
25494         var range = this.createRange(sel);
25495          
25496         try {
25497             var p = range.commonAncestorContainer;
25498             while (p.nodeType == 3) { // text node
25499                 p = p.parentNode;
25500             }
25501             return p;
25502         } catch (e) {
25503             return null;
25504         }
25505     
25506     },
25507     /***
25508      *
25509      * Range intersection.. the hard stuff...
25510      *  '-1' = before
25511      *  '0' = hits..
25512      *  '1' = after.
25513      *         [ -- selected range --- ]
25514      *   [fail]                        [fail]
25515      *
25516      *    basically..
25517      *      if end is before start or  hits it. fail.
25518      *      if start is after end or hits it fail.
25519      *
25520      *   if either hits (but other is outside. - then it's not 
25521      *   
25522      *    
25523      **/
25524     
25525     
25526     // @see http://www.thismuchiknow.co.uk/?p=64.
25527     rangeIntersectsNode : function(range, node)
25528     {
25529         var nodeRange = node.ownerDocument.createRange();
25530         try {
25531             nodeRange.selectNode(node);
25532         } catch (e) {
25533             nodeRange.selectNodeContents(node);
25534         }
25535     
25536         var rangeStartRange = range.cloneRange();
25537         rangeStartRange.collapse(true);
25538     
25539         var rangeEndRange = range.cloneRange();
25540         rangeEndRange.collapse(false);
25541     
25542         var nodeStartRange = nodeRange.cloneRange();
25543         nodeStartRange.collapse(true);
25544     
25545         var nodeEndRange = nodeRange.cloneRange();
25546         nodeEndRange.collapse(false);
25547     
25548         return rangeStartRange.compareBoundaryPoints(
25549                  Range.START_TO_START, nodeEndRange) == -1 &&
25550                rangeEndRange.compareBoundaryPoints(
25551                  Range.START_TO_START, nodeStartRange) == 1;
25552         
25553          
25554     },
25555     rangeCompareNode : function(range, node)
25556     {
25557         var nodeRange = node.ownerDocument.createRange();
25558         try {
25559             nodeRange.selectNode(node);
25560         } catch (e) {
25561             nodeRange.selectNodeContents(node);
25562         }
25563         
25564         
25565         range.collapse(true);
25566     
25567         nodeRange.collapse(true);
25568      
25569         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25570         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25571          
25572         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25573         
25574         var nodeIsBefore   =  ss == 1;
25575         var nodeIsAfter    = ee == -1;
25576         
25577         if (nodeIsBefore && nodeIsAfter)
25578             return 0; // outer
25579         if (!nodeIsBefore && nodeIsAfter)
25580             return 1; //right trailed.
25581         
25582         if (nodeIsBefore && !nodeIsAfter)
25583             return 2;  // left trailed.
25584         // fully contined.
25585         return 3;
25586     },
25587
25588     // private? - in a new class?
25589     cleanUpPaste :  function()
25590     {
25591         // cleans up the whole document..
25592         Roo.log('cleanuppaste');
25593         
25594         this.cleanUpChildren(this.doc.body);
25595         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25596         if (clean != this.doc.body.innerHTML) {
25597             this.doc.body.innerHTML = clean;
25598         }
25599         
25600     },
25601     
25602     cleanWordChars : function(input) {// change the chars to hex code
25603         var he = Roo.HtmlEditorCore;
25604         
25605         var output = input;
25606         Roo.each(he.swapCodes, function(sw) { 
25607             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25608             
25609             output = output.replace(swapper, sw[1]);
25610         });
25611         
25612         return output;
25613     },
25614     
25615     
25616     cleanUpChildren : function (n)
25617     {
25618         if (!n.childNodes.length) {
25619             return;
25620         }
25621         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25622            this.cleanUpChild(n.childNodes[i]);
25623         }
25624     },
25625     
25626     
25627         
25628     
25629     cleanUpChild : function (node)
25630     {
25631         var ed = this;
25632         //console.log(node);
25633         if (node.nodeName == "#text") {
25634             // clean up silly Windows -- stuff?
25635             return; 
25636         }
25637         if (node.nodeName == "#comment") {
25638             node.parentNode.removeChild(node);
25639             // clean up silly Windows -- stuff?
25640             return; 
25641         }
25642         var lcname = node.tagName.toLowerCase();
25643         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25644         // whitelist of tags..
25645         
25646         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25647             // remove node.
25648             node.parentNode.removeChild(node);
25649             return;
25650             
25651         }
25652         
25653         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25654         
25655         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25656         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25657         
25658         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25659         //    remove_keep_children = true;
25660         //}
25661         
25662         if (remove_keep_children) {
25663             this.cleanUpChildren(node);
25664             // inserts everything just before this node...
25665             while (node.childNodes.length) {
25666                 var cn = node.childNodes[0];
25667                 node.removeChild(cn);
25668                 node.parentNode.insertBefore(cn, node);
25669             }
25670             node.parentNode.removeChild(node);
25671             return;
25672         }
25673         
25674         if (!node.attributes || !node.attributes.length) {
25675             this.cleanUpChildren(node);
25676             return;
25677         }
25678         
25679         function cleanAttr(n,v)
25680         {
25681             
25682             if (v.match(/^\./) || v.match(/^\//)) {
25683                 return;
25684             }
25685             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25686                 return;
25687             }
25688             if (v.match(/^#/)) {
25689                 return;
25690             }
25691 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25692             node.removeAttribute(n);
25693             
25694         }
25695         
25696         var cwhite = this.cwhite;
25697         var cblack = this.cblack;
25698             
25699         function cleanStyle(n,v)
25700         {
25701             if (v.match(/expression/)) { //XSS?? should we even bother..
25702                 node.removeAttribute(n);
25703                 return;
25704             }
25705             
25706             var parts = v.split(/;/);
25707             var clean = [];
25708             
25709             Roo.each(parts, function(p) {
25710                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25711                 if (!p.length) {
25712                     return true;
25713                 }
25714                 var l = p.split(':').shift().replace(/\s+/g,'');
25715                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25716                 
25717                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25718 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25719                     //node.removeAttribute(n);
25720                     return true;
25721                 }
25722                 //Roo.log()
25723                 // only allow 'c whitelisted system attributes'
25724                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25725 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25726                     //node.removeAttribute(n);
25727                     return true;
25728                 }
25729                 
25730                 
25731                  
25732                 
25733                 clean.push(p);
25734                 return true;
25735             });
25736             if (clean.length) { 
25737                 node.setAttribute(n, clean.join(';'));
25738             } else {
25739                 node.removeAttribute(n);
25740             }
25741             
25742         }
25743         
25744         
25745         for (var i = node.attributes.length-1; i > -1 ; i--) {
25746             var a = node.attributes[i];
25747             //console.log(a);
25748             
25749             if (a.name.toLowerCase().substr(0,2)=='on')  {
25750                 node.removeAttribute(a.name);
25751                 continue;
25752             }
25753             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25754                 node.removeAttribute(a.name);
25755                 continue;
25756             }
25757             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25758                 cleanAttr(a.name,a.value); // fixme..
25759                 continue;
25760             }
25761             if (a.name == 'style') {
25762                 cleanStyle(a.name,a.value);
25763                 continue;
25764             }
25765             /// clean up MS crap..
25766             // tecnically this should be a list of valid class'es..
25767             
25768             
25769             if (a.name == 'class') {
25770                 if (a.value.match(/^Mso/)) {
25771                     node.className = '';
25772                 }
25773                 
25774                 if (a.value.match(/body/)) {
25775                     node.className = '';
25776                 }
25777                 continue;
25778             }
25779             
25780             // style cleanup!?
25781             // class cleanup?
25782             
25783         }
25784         
25785         
25786         this.cleanUpChildren(node);
25787         
25788         
25789     },
25790     /**
25791      * Clean up MS wordisms...
25792      */
25793     cleanWord : function(node)
25794     {
25795         var _t = this;
25796         var cleanWordChildren = function()
25797         {
25798             if (!node.childNodes.length) {
25799                 return;
25800             }
25801             for (var i = node.childNodes.length-1; i > -1 ; i--) {
25802                _t.cleanWord(node.childNodes[i]);
25803             }
25804         }
25805         
25806         
25807         if (!node) {
25808             this.cleanWord(this.doc.body);
25809             return;
25810         }
25811         if (node.nodeName == "#text") {
25812             // clean up silly Windows -- stuff?
25813             return; 
25814         }
25815         if (node.nodeName == "#comment") {
25816             node.parentNode.removeChild(node);
25817             // clean up silly Windows -- stuff?
25818             return; 
25819         }
25820         
25821         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25822             node.parentNode.removeChild(node);
25823             return;
25824         }
25825         
25826         // remove - but keep children..
25827         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25828             while (node.childNodes.length) {
25829                 var cn = node.childNodes[0];
25830                 node.removeChild(cn);
25831                 node.parentNode.insertBefore(cn, node);
25832             }
25833             node.parentNode.removeChild(node);
25834             cleanWordChildren();
25835             return;
25836         }
25837         // clean styles
25838         if (node.className.length) {
25839             
25840             var cn = node.className.split(/\W+/);
25841             var cna = [];
25842             Roo.each(cn, function(cls) {
25843                 if (cls.match(/Mso[a-zA-Z]+/)) {
25844                     return;
25845                 }
25846                 cna.push(cls);
25847             });
25848             node.className = cna.length ? cna.join(' ') : '';
25849             if (!cna.length) {
25850                 node.removeAttribute("class");
25851             }
25852         }
25853         
25854         if (node.hasAttribute("lang")) {
25855             node.removeAttribute("lang");
25856         }
25857         
25858         if (node.hasAttribute("style")) {
25859             
25860             var styles = node.getAttribute("style").split(";");
25861             var nstyle = [];
25862             Roo.each(styles, function(s) {
25863                 if (!s.match(/:/)) {
25864                     return;
25865                 }
25866                 var kv = s.split(":");
25867                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25868                     return;
25869                 }
25870                 // what ever is left... we allow.
25871                 nstyle.push(s);
25872             });
25873             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25874             if (!nstyle.length) {
25875                 node.removeAttribute('style');
25876             }
25877         }
25878         
25879         cleanWordChildren();
25880         
25881         
25882     },
25883     domToHTML : function(currentElement, depth, nopadtext) {
25884         
25885         depth = depth || 0;
25886         nopadtext = nopadtext || false;
25887     
25888         if (!currentElement) {
25889             return this.domToHTML(this.doc.body);
25890         }
25891         
25892         //Roo.log(currentElement);
25893         var j;
25894         var allText = false;
25895         var nodeName = currentElement.nodeName;
25896         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25897         
25898         if  (nodeName == '#text') {
25899             return currentElement.nodeValue;
25900         }
25901         
25902         
25903         var ret = '';
25904         if (nodeName != 'BODY') {
25905              
25906             var i = 0;
25907             // Prints the node tagName, such as <A>, <IMG>, etc
25908             if (tagName) {
25909                 var attr = [];
25910                 for(i = 0; i < currentElement.attributes.length;i++) {
25911                     // quoting?
25912                     var aname = currentElement.attributes.item(i).name;
25913                     if (!currentElement.attributes.item(i).value.length) {
25914                         continue;
25915                     }
25916                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25917                 }
25918                 
25919                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25920             } 
25921             else {
25922                 
25923                 // eack
25924             }
25925         } else {
25926             tagName = false;
25927         }
25928         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25929             return ret;
25930         }
25931         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25932             nopadtext = true;
25933         }
25934         
25935         
25936         // Traverse the tree
25937         i = 0;
25938         var currentElementChild = currentElement.childNodes.item(i);
25939         var allText = true;
25940         var innerHTML  = '';
25941         lastnode = '';
25942         while (currentElementChild) {
25943             // Formatting code (indent the tree so it looks nice on the screen)
25944             var nopad = nopadtext;
25945             if (lastnode == 'SPAN') {
25946                 nopad  = true;
25947             }
25948             // text
25949             if  (currentElementChild.nodeName == '#text') {
25950                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25951                 if (!nopad && toadd.length > 80) {
25952                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25953                 }
25954                 innerHTML  += toadd;
25955                 
25956                 i++;
25957                 currentElementChild = currentElement.childNodes.item(i);
25958                 lastNode = '';
25959                 continue;
25960             }
25961             allText = false;
25962             
25963             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25964                 
25965             // Recursively traverse the tree structure of the child node
25966             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25967             lastnode = currentElementChild.nodeName;
25968             i++;
25969             currentElementChild=currentElement.childNodes.item(i);
25970         }
25971         
25972         ret += innerHTML;
25973         
25974         if (!allText) {
25975                 // The remaining code is mostly for formatting the tree
25976             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25977         }
25978         
25979         
25980         if (tagName) {
25981             ret+= "</"+tagName+">";
25982         }
25983         return ret;
25984         
25985     },
25986         
25987     applyBlacklists : function()
25988     {
25989         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25990         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25991         
25992         this.white = [];
25993         this.black = [];
25994         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25995             if (b.indexOf(tag) > -1) {
25996                 return;
25997             }
25998             this.white.push(tag);
25999             
26000         }, this);
26001         
26002         Roo.each(w, function(tag) {
26003             if (b.indexOf(tag) > -1) {
26004                 return;
26005             }
26006             if (this.white.indexOf(tag) > -1) {
26007                 return;
26008             }
26009             this.white.push(tag);
26010             
26011         }, this);
26012         
26013         
26014         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26015             if (w.indexOf(tag) > -1) {
26016                 return;
26017             }
26018             this.black.push(tag);
26019             
26020         }, this);
26021         
26022         Roo.each(b, function(tag) {
26023             if (w.indexOf(tag) > -1) {
26024                 return;
26025             }
26026             if (this.black.indexOf(tag) > -1) {
26027                 return;
26028             }
26029             this.black.push(tag);
26030             
26031         }, this);
26032         
26033         
26034         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26035         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26036         
26037         this.cwhite = [];
26038         this.cblack = [];
26039         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26040             if (b.indexOf(tag) > -1) {
26041                 return;
26042             }
26043             this.cwhite.push(tag);
26044             
26045         }, this);
26046         
26047         Roo.each(w, function(tag) {
26048             if (b.indexOf(tag) > -1) {
26049                 return;
26050             }
26051             if (this.cwhite.indexOf(tag) > -1) {
26052                 return;
26053             }
26054             this.cwhite.push(tag);
26055             
26056         }, this);
26057         
26058         
26059         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26060             if (w.indexOf(tag) > -1) {
26061                 return;
26062             }
26063             this.cblack.push(tag);
26064             
26065         }, this);
26066         
26067         Roo.each(b, function(tag) {
26068             if (w.indexOf(tag) > -1) {
26069                 return;
26070             }
26071             if (this.cblack.indexOf(tag) > -1) {
26072                 return;
26073             }
26074             this.cblack.push(tag);
26075             
26076         }, this);
26077     }
26078     
26079     // hide stuff that is not compatible
26080     /**
26081      * @event blur
26082      * @hide
26083      */
26084     /**
26085      * @event change
26086      * @hide
26087      */
26088     /**
26089      * @event focus
26090      * @hide
26091      */
26092     /**
26093      * @event specialkey
26094      * @hide
26095      */
26096     /**
26097      * @cfg {String} fieldClass @hide
26098      */
26099     /**
26100      * @cfg {String} focusClass @hide
26101      */
26102     /**
26103      * @cfg {String} autoCreate @hide
26104      */
26105     /**
26106      * @cfg {String} inputType @hide
26107      */
26108     /**
26109      * @cfg {String} invalidClass @hide
26110      */
26111     /**
26112      * @cfg {String} invalidText @hide
26113      */
26114     /**
26115      * @cfg {String} msgFx @hide
26116      */
26117     /**
26118      * @cfg {String} validateOnBlur @hide
26119      */
26120 });
26121
26122 Roo.HtmlEditorCore.white = [
26123         'area', 'br', 'img', 'input', 'hr', 'wbr',
26124         
26125        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26126        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26127        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26128        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26129        'table',   'ul',         'xmp', 
26130        
26131        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26132       'thead',   'tr', 
26133      
26134       'dir', 'menu', 'ol', 'ul', 'dl',
26135        
26136       'embed',  'object'
26137 ];
26138
26139
26140 Roo.HtmlEditorCore.black = [
26141     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26142         'applet', // 
26143         'base',   'basefont', 'bgsound', 'blink',  'body', 
26144         'frame',  'frameset', 'head',    'html',   'ilayer', 
26145         'iframe', 'layer',  'link',     'meta',    'object',   
26146         'script', 'style' ,'title',  'xml' // clean later..
26147 ];
26148 Roo.HtmlEditorCore.clean = [
26149     'script', 'style', 'title', 'xml'
26150 ];
26151 Roo.HtmlEditorCore.remove = [
26152     'font'
26153 ];
26154 // attributes..
26155
26156 Roo.HtmlEditorCore.ablack = [
26157     'on'
26158 ];
26159     
26160 Roo.HtmlEditorCore.aclean = [ 
26161     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26162 ];
26163
26164 // protocols..
26165 Roo.HtmlEditorCore.pwhite= [
26166         'http',  'https',  'mailto'
26167 ];
26168
26169 // white listed style attributes.
26170 Roo.HtmlEditorCore.cwhite= [
26171       //  'text-align', /// default is to allow most things..
26172       
26173          
26174 //        'font-size'//??
26175 ];
26176
26177 // black listed style attributes.
26178 Roo.HtmlEditorCore.cblack= [
26179       //  'font-size' -- this can be set by the project 
26180 ];
26181
26182
26183 Roo.HtmlEditorCore.swapCodes   =[ 
26184     [    8211, "--" ], 
26185     [    8212, "--" ], 
26186     [    8216,  "'" ],  
26187     [    8217, "'" ],  
26188     [    8220, '"' ],  
26189     [    8221, '"' ],  
26190     [    8226, "*" ],  
26191     [    8230, "..." ]
26192 ]; 
26193
26194     //<script type="text/javascript">
26195
26196 /*
26197  * Ext JS Library 1.1.1
26198  * Copyright(c) 2006-2007, Ext JS, LLC.
26199  * Licence LGPL
26200  * 
26201  */
26202  
26203  
26204 Roo.form.HtmlEditor = function(config){
26205     
26206     
26207     
26208     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26209     
26210     if (!this.toolbars) {
26211         this.toolbars = [];
26212     }
26213     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26214     
26215     
26216 };
26217
26218 /**
26219  * @class Roo.form.HtmlEditor
26220  * @extends Roo.form.Field
26221  * Provides a lightweight HTML Editor component.
26222  *
26223  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26224  * 
26225  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26226  * supported by this editor.</b><br/><br/>
26227  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26228  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26229  */
26230 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26231     /**
26232      * @cfg {Boolean} clearUp
26233      */
26234     clearUp : true,
26235       /**
26236      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26237      */
26238     toolbars : false,
26239    
26240      /**
26241      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26242      *                        Roo.resizable.
26243      */
26244     resizable : false,
26245      /**
26246      * @cfg {Number} height (in pixels)
26247      */   
26248     height: 300,
26249    /**
26250      * @cfg {Number} width (in pixels)
26251      */   
26252     width: 500,
26253     
26254     /**
26255      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26256      * 
26257      */
26258     stylesheets: false,
26259     
26260     
26261      /**
26262      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26263      * 
26264      */
26265     cblack: false,
26266     /**
26267      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26268      * 
26269      */
26270     cwhite: false,
26271     
26272      /**
26273      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26274      * 
26275      */
26276     black: false,
26277     /**
26278      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26279      * 
26280      */
26281     white: false,
26282     
26283     // id of frame..
26284     frameId: false,
26285     
26286     // private properties
26287     validationEvent : false,
26288     deferHeight: true,
26289     initialized : false,
26290     activated : false,
26291     
26292     onFocus : Roo.emptyFn,
26293     iframePad:3,
26294     hideMode:'offsets',
26295     
26296     actionMode : 'container', // defaults to hiding it...
26297     
26298     defaultAutoCreate : { // modified by initCompnoent..
26299         tag: "textarea",
26300         style:"width:500px;height:300px;",
26301         autocomplete: "off"
26302     },
26303
26304     // private
26305     initComponent : function(){
26306         this.addEvents({
26307             /**
26308              * @event initialize
26309              * Fires when the editor is fully initialized (including the iframe)
26310              * @param {HtmlEditor} this
26311              */
26312             initialize: true,
26313             /**
26314              * @event activate
26315              * Fires when the editor is first receives the focus. Any insertion must wait
26316              * until after this event.
26317              * @param {HtmlEditor} this
26318              */
26319             activate: true,
26320              /**
26321              * @event beforesync
26322              * Fires before the textarea is updated with content from the editor iframe. Return false
26323              * to cancel the sync.
26324              * @param {HtmlEditor} this
26325              * @param {String} html
26326              */
26327             beforesync: true,
26328              /**
26329              * @event beforepush
26330              * Fires before the iframe editor is updated with content from the textarea. Return false
26331              * to cancel the push.
26332              * @param {HtmlEditor} this
26333              * @param {String} html
26334              */
26335             beforepush: true,
26336              /**
26337              * @event sync
26338              * Fires when the textarea is updated with content from the editor iframe.
26339              * @param {HtmlEditor} this
26340              * @param {String} html
26341              */
26342             sync: true,
26343              /**
26344              * @event push
26345              * Fires when the iframe editor is updated with content from the textarea.
26346              * @param {HtmlEditor} this
26347              * @param {String} html
26348              */
26349             push: true,
26350              /**
26351              * @event editmodechange
26352              * Fires when the editor switches edit modes
26353              * @param {HtmlEditor} this
26354              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26355              */
26356             editmodechange: true,
26357             /**
26358              * @event editorevent
26359              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26360              * @param {HtmlEditor} this
26361              */
26362             editorevent: true,
26363             /**
26364              * @event firstfocus
26365              * Fires when on first focus - needed by toolbars..
26366              * @param {HtmlEditor} this
26367              */
26368             firstfocus: true,
26369             /**
26370              * @event autosave
26371              * Auto save the htmlEditor value as a file into Events
26372              * @param {HtmlEditor} this
26373              */
26374             autosave: true,
26375             /**
26376              * @event savedpreview
26377              * preview the saved version of htmlEditor
26378              * @param {HtmlEditor} this
26379              */
26380             savedpreview: true
26381         });
26382         this.defaultAutoCreate =  {
26383             tag: "textarea",
26384             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26385             autocomplete: "off"
26386         };
26387     },
26388
26389     /**
26390      * Protected method that will not generally be called directly. It
26391      * is called when the editor creates its toolbar. Override this method if you need to
26392      * add custom toolbar buttons.
26393      * @param {HtmlEditor} editor
26394      */
26395     createToolbar : function(editor){
26396         Roo.log("create toolbars");
26397         if (!editor.toolbars || !editor.toolbars.length) {
26398             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26399         }
26400         
26401         for (var i =0 ; i < editor.toolbars.length;i++) {
26402             editor.toolbars[i] = Roo.factory(
26403                     typeof(editor.toolbars[i]) == 'string' ?
26404                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26405                 Roo.form.HtmlEditor);
26406             editor.toolbars[i].init(editor);
26407         }
26408          
26409         
26410     },
26411
26412      
26413     // private
26414     onRender : function(ct, position)
26415     {
26416         var _t = this;
26417         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26418         
26419         this.wrap = this.el.wrap({
26420             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26421         });
26422         
26423         this.editorcore.onRender(ct, position);
26424          
26425         if (this.resizable) {
26426             this.resizeEl = new Roo.Resizable(this.wrap, {
26427                 pinned : true,
26428                 wrap: true,
26429                 dynamic : true,
26430                 minHeight : this.height,
26431                 height: this.height,
26432                 handles : this.resizable,
26433                 width: this.width,
26434                 listeners : {
26435                     resize : function(r, w, h) {
26436                         _t.onResize(w,h); // -something
26437                     }
26438                 }
26439             });
26440             
26441         }
26442         this.createToolbar(this);
26443        
26444         
26445         if(!this.width){
26446             this.setSize(this.wrap.getSize());
26447         }
26448         if (this.resizeEl) {
26449             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26450             // should trigger onReize..
26451         }
26452         
26453 //        if(this.autosave && this.w){
26454 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26455 //        }
26456     },
26457
26458     // private
26459     onResize : function(w, h)
26460     {
26461         //Roo.log('resize: ' +w + ',' + h );
26462         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26463         var ew = false;
26464         var eh = false;
26465         
26466         if(this.el ){
26467             if(typeof w == 'number'){
26468                 var aw = w - this.wrap.getFrameWidth('lr');
26469                 this.el.setWidth(this.adjustWidth('textarea', aw));
26470                 ew = aw;
26471             }
26472             if(typeof h == 'number'){
26473                 var tbh = 0;
26474                 for (var i =0; i < this.toolbars.length;i++) {
26475                     // fixme - ask toolbars for heights?
26476                     tbh += this.toolbars[i].tb.el.getHeight();
26477                     if (this.toolbars[i].footer) {
26478                         tbh += this.toolbars[i].footer.el.getHeight();
26479                     }
26480                 }
26481                 
26482                 
26483                 
26484                 
26485                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26486                 ah -= 5; // knock a few pixes off for look..
26487                 this.el.setHeight(this.adjustWidth('textarea', ah));
26488                 var eh = ah;
26489             }
26490         }
26491         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26492         this.editorcore.onResize(ew,eh);
26493         
26494     },
26495
26496     /**
26497      * Toggles the editor between standard and source edit mode.
26498      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26499      */
26500     toggleSourceEdit : function(sourceEditMode)
26501     {
26502         this.editorcore.toggleSourceEdit(sourceEditMode);
26503         
26504         if(this.editorcore.sourceEditMode){
26505             Roo.log('editor - showing textarea');
26506             
26507 //            Roo.log('in');
26508 //            Roo.log(this.syncValue());
26509             this.editorcore.syncValue();
26510             this.el.removeClass('x-hidden');
26511             this.el.dom.removeAttribute('tabIndex');
26512             this.el.focus();
26513         }else{
26514             Roo.log('editor - hiding textarea');
26515 //            Roo.log('out')
26516 //            Roo.log(this.pushValue()); 
26517             this.editorcore.pushValue();
26518             
26519             this.el.addClass('x-hidden');
26520             this.el.dom.setAttribute('tabIndex', -1);
26521             //this.deferFocus();
26522         }
26523          
26524         this.setSize(this.wrap.getSize());
26525         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26526     },
26527  
26528     // private (for BoxComponent)
26529     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26530
26531     // private (for BoxComponent)
26532     getResizeEl : function(){
26533         return this.wrap;
26534     },
26535
26536     // private (for BoxComponent)
26537     getPositionEl : function(){
26538         return this.wrap;
26539     },
26540
26541     // private
26542     initEvents : function(){
26543         this.originalValue = this.getValue();
26544     },
26545
26546     /**
26547      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26548      * @method
26549      */
26550     markInvalid : Roo.emptyFn,
26551     /**
26552      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26553      * @method
26554      */
26555     clearInvalid : Roo.emptyFn,
26556
26557     setValue : function(v){
26558         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26559         this.editorcore.pushValue();
26560     },
26561
26562      
26563     // private
26564     deferFocus : function(){
26565         this.focus.defer(10, this);
26566     },
26567
26568     // doc'ed in Field
26569     focus : function(){
26570         this.editorcore.focus();
26571         
26572     },
26573       
26574
26575     // private
26576     onDestroy : function(){
26577         
26578         
26579         
26580         if(this.rendered){
26581             
26582             for (var i =0; i < this.toolbars.length;i++) {
26583                 // fixme - ask toolbars for heights?
26584                 this.toolbars[i].onDestroy();
26585             }
26586             
26587             this.wrap.dom.innerHTML = '';
26588             this.wrap.remove();
26589         }
26590     },
26591
26592     // private
26593     onFirstFocus : function(){
26594         //Roo.log("onFirstFocus");
26595         this.editorcore.onFirstFocus();
26596          for (var i =0; i < this.toolbars.length;i++) {
26597             this.toolbars[i].onFirstFocus();
26598         }
26599         
26600     },
26601     
26602     // private
26603     syncValue : function()
26604     {
26605         this.editorcore.syncValue();
26606     },
26607     
26608     pushValue : function()
26609     {
26610         this.editorcore.pushValue();
26611     }
26612      
26613     
26614     // hide stuff that is not compatible
26615     /**
26616      * @event blur
26617      * @hide
26618      */
26619     /**
26620      * @event change
26621      * @hide
26622      */
26623     /**
26624      * @event focus
26625      * @hide
26626      */
26627     /**
26628      * @event specialkey
26629      * @hide
26630      */
26631     /**
26632      * @cfg {String} fieldClass @hide
26633      */
26634     /**
26635      * @cfg {String} focusClass @hide
26636      */
26637     /**
26638      * @cfg {String} autoCreate @hide
26639      */
26640     /**
26641      * @cfg {String} inputType @hide
26642      */
26643     /**
26644      * @cfg {String} invalidClass @hide
26645      */
26646     /**
26647      * @cfg {String} invalidText @hide
26648      */
26649     /**
26650      * @cfg {String} msgFx @hide
26651      */
26652     /**
26653      * @cfg {String} validateOnBlur @hide
26654      */
26655 });
26656  
26657     // <script type="text/javascript">
26658 /*
26659  * Based on
26660  * Ext JS Library 1.1.1
26661  * Copyright(c) 2006-2007, Ext JS, LLC.
26662  *  
26663  
26664  */
26665
26666 /**
26667  * @class Roo.form.HtmlEditorToolbar1
26668  * Basic Toolbar
26669  * 
26670  * Usage:
26671  *
26672  new Roo.form.HtmlEditor({
26673     ....
26674     toolbars : [
26675         new Roo.form.HtmlEditorToolbar1({
26676             disable : { fonts: 1 , format: 1, ..., ... , ...],
26677             btns : [ .... ]
26678         })
26679     }
26680      
26681  * 
26682  * @cfg {Object} disable List of elements to disable..
26683  * @cfg {Array} btns List of additional buttons.
26684  * 
26685  * 
26686  * NEEDS Extra CSS? 
26687  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26688  */
26689  
26690 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26691 {
26692     
26693     Roo.apply(this, config);
26694     
26695     // default disabled, based on 'good practice'..
26696     this.disable = this.disable || {};
26697     Roo.applyIf(this.disable, {
26698         fontSize : true,
26699         colors : true,
26700         specialElements : true
26701     });
26702     
26703     
26704     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26705     // dont call parent... till later.
26706 }
26707
26708 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26709     
26710     tb: false,
26711     
26712     rendered: false,
26713     
26714     editor : false,
26715     editorcore : false,
26716     /**
26717      * @cfg {Object} disable  List of toolbar elements to disable
26718          
26719      */
26720     disable : false,
26721     
26722     
26723      /**
26724      * @cfg {String} createLinkText The default text for the create link prompt
26725      */
26726     createLinkText : 'Please enter the URL for the link:',
26727     /**
26728      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
26729      */
26730     defaultLinkValue : 'http:/'+'/',
26731    
26732     
26733       /**
26734      * @cfg {Array} fontFamilies An array of available font families
26735      */
26736     fontFamilies : [
26737         'Arial',
26738         'Courier New',
26739         'Tahoma',
26740         'Times New Roman',
26741         'Verdana'
26742     ],
26743     
26744     specialChars : [
26745            "&#169;",
26746           "&#174;",     
26747           "&#8482;",    
26748           "&#163;" ,    
26749          // "&#8212;",    
26750           "&#8230;",    
26751           "&#247;" ,    
26752         //  "&#225;" ,     ?? a acute?
26753            "&#8364;"    , //Euro
26754        //   "&#8220;"    ,
26755         //  "&#8221;"    ,
26756         //  "&#8226;"    ,
26757           "&#176;"  //   , // degrees
26758
26759          // "&#233;"     , // e ecute
26760          // "&#250;"     , // u ecute?
26761     ],
26762     
26763     specialElements : [
26764         {
26765             text: "Insert Table",
26766             xtype: 'MenuItem',
26767             xns : Roo.Menu,
26768             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26769                 
26770         },
26771         {    
26772             text: "Insert Image",
26773             xtype: 'MenuItem',
26774             xns : Roo.Menu,
26775             ihtml : '<img src="about:blank"/>'
26776             
26777         }
26778         
26779          
26780     ],
26781     
26782     
26783     inputElements : [ 
26784             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26785             "input:submit", "input:button", "select", "textarea", "label" ],
26786     formats : [
26787         ["p"] ,  
26788         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26789         ["pre"],[ "code"], 
26790         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26791         ['div'],['span']
26792     ],
26793     
26794     cleanStyles : [
26795         "font-size"
26796     ],
26797      /**
26798      * @cfg {String} defaultFont default font to use.
26799      */
26800     defaultFont: 'tahoma',
26801    
26802     fontSelect : false,
26803     
26804     
26805     formatCombo : false,
26806     
26807     init : function(editor)
26808     {
26809         this.editor = editor;
26810         this.editorcore = editor.editorcore ? editor.editorcore : editor;
26811         var editorcore = this.editorcore;
26812         
26813         var _t = this;
26814         
26815         var fid = editorcore.frameId;
26816         var etb = this;
26817         function btn(id, toggle, handler){
26818             var xid = fid + '-'+ id ;
26819             return {
26820                 id : xid,
26821                 cmd : id,
26822                 cls : 'x-btn-icon x-edit-'+id,
26823                 enableToggle:toggle !== false,
26824                 scope: _t, // was editor...
26825                 handler:handler||_t.relayBtnCmd,
26826                 clickEvent:'mousedown',
26827                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26828                 tabIndex:-1
26829             };
26830         }
26831         
26832         
26833         
26834         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26835         this.tb = tb;
26836          // stop form submits
26837         tb.el.on('click', function(e){
26838             e.preventDefault(); // what does this do?
26839         });
26840
26841         if(!this.disable.font) { // && !Roo.isSafari){
26842             /* why no safari for fonts 
26843             editor.fontSelect = tb.el.createChild({
26844                 tag:'select',
26845                 tabIndex: -1,
26846                 cls:'x-font-select',
26847                 html: this.createFontOptions()
26848             });
26849             
26850             editor.fontSelect.on('change', function(){
26851                 var font = editor.fontSelect.dom.value;
26852                 editor.relayCmd('fontname', font);
26853                 editor.deferFocus();
26854             }, editor);
26855             
26856             tb.add(
26857                 editor.fontSelect.dom,
26858                 '-'
26859             );
26860             */
26861             
26862         };
26863         if(!this.disable.formats){
26864             this.formatCombo = new Roo.form.ComboBox({
26865                 store: new Roo.data.SimpleStore({
26866                     id : 'tag',
26867                     fields: ['tag'],
26868                     data : this.formats // from states.js
26869                 }),
26870                 blockFocus : true,
26871                 name : '',
26872                 //autoCreate : {tag: "div",  size: "20"},
26873                 displayField:'tag',
26874                 typeAhead: false,
26875                 mode: 'local',
26876                 editable : false,
26877                 triggerAction: 'all',
26878                 emptyText:'Add tag',
26879                 selectOnFocus:true,
26880                 width:135,
26881                 listeners : {
26882                     'select': function(c, r, i) {
26883                         editorcore.insertTag(r.get('tag'));
26884                         editor.focus();
26885                     }
26886                 }
26887
26888             });
26889             tb.addField(this.formatCombo);
26890             
26891         }
26892         
26893         if(!this.disable.format){
26894             tb.add(
26895                 btn('bold'),
26896                 btn('italic'),
26897                 btn('underline')
26898             );
26899         };
26900         if(!this.disable.fontSize){
26901             tb.add(
26902                 '-',
26903                 
26904                 
26905                 btn('increasefontsize', false, editorcore.adjustFont),
26906                 btn('decreasefontsize', false, editorcore.adjustFont)
26907             );
26908         };
26909         
26910         
26911         if(!this.disable.colors){
26912             tb.add(
26913                 '-', {
26914                     id:editorcore.frameId +'-forecolor',
26915                     cls:'x-btn-icon x-edit-forecolor',
26916                     clickEvent:'mousedown',
26917                     tooltip: this.buttonTips['forecolor'] || undefined,
26918                     tabIndex:-1,
26919                     menu : new Roo.menu.ColorMenu({
26920                         allowReselect: true,
26921                         focus: Roo.emptyFn,
26922                         value:'000000',
26923                         plain:true,
26924                         selectHandler: function(cp, color){
26925                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26926                             editor.deferFocus();
26927                         },
26928                         scope: editorcore,
26929                         clickEvent:'mousedown'
26930                     })
26931                 }, {
26932                     id:editorcore.frameId +'backcolor',
26933                     cls:'x-btn-icon x-edit-backcolor',
26934                     clickEvent:'mousedown',
26935                     tooltip: this.buttonTips['backcolor'] || undefined,
26936                     tabIndex:-1,
26937                     menu : new Roo.menu.ColorMenu({
26938                         focus: Roo.emptyFn,
26939                         value:'FFFFFF',
26940                         plain:true,
26941                         allowReselect: true,
26942                         selectHandler: function(cp, color){
26943                             if(Roo.isGecko){
26944                                 editorcore.execCmd('useCSS', false);
26945                                 editorcore.execCmd('hilitecolor', color);
26946                                 editorcore.execCmd('useCSS', true);
26947                                 editor.deferFocus();
26948                             }else{
26949                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26950                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26951                                 editor.deferFocus();
26952                             }
26953                         },
26954                         scope:editorcore,
26955                         clickEvent:'mousedown'
26956                     })
26957                 }
26958             );
26959         };
26960         // now add all the items...
26961         
26962
26963         if(!this.disable.alignments){
26964             tb.add(
26965                 '-',
26966                 btn('justifyleft'),
26967                 btn('justifycenter'),
26968                 btn('justifyright')
26969             );
26970         };
26971
26972         //if(!Roo.isSafari){
26973             if(!this.disable.links){
26974                 tb.add(
26975                     '-',
26976                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
26977                 );
26978             };
26979
26980             if(!this.disable.lists){
26981                 tb.add(
26982                     '-',
26983                     btn('insertorderedlist'),
26984                     btn('insertunorderedlist')
26985                 );
26986             }
26987             if(!this.disable.sourceEdit){
26988                 tb.add(
26989                     '-',
26990                     btn('sourceedit', true, function(btn){
26991                         Roo.log(this);
26992                         this.toggleSourceEdit(btn.pressed);
26993                     })
26994                 );
26995             }
26996         //}
26997         
26998         var smenu = { };
26999         // special menu.. - needs to be tidied up..
27000         if (!this.disable.special) {
27001             smenu = {
27002                 text: "&#169;",
27003                 cls: 'x-edit-none',
27004                 
27005                 menu : {
27006                     items : []
27007                 }
27008             };
27009             for (var i =0; i < this.specialChars.length; i++) {
27010                 smenu.menu.items.push({
27011                     
27012                     html: this.specialChars[i],
27013                     handler: function(a,b) {
27014                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27015                         //editor.insertAtCursor(a.html);
27016                         
27017                     },
27018                     tabIndex:-1
27019                 });
27020             }
27021             
27022             
27023             tb.add(smenu);
27024             
27025             
27026         }
27027         
27028         var cmenu = { };
27029         if (!this.disable.cleanStyles) {
27030             cmenu = {
27031                 cls: 'x-btn-icon x-btn-clear',
27032                 
27033                 menu : {
27034                     items : []
27035                 }
27036             };
27037             for (var i =0; i < this.cleanStyles.length; i++) {
27038                 cmenu.menu.items.push({
27039                     actiontype : this.cleanStyles[i],
27040                     html: 'Remove ' + this.cleanStyles[i],
27041                     handler: function(a,b) {
27042                         Roo.log(a);
27043                         Roo.log(b);
27044                         var c = Roo.get(editorcore.doc.body);
27045                         c.select('[style]').each(function(s) {
27046                             s.dom.style.removeProperty(a.actiontype);
27047                         });
27048                         editorcore.syncValue();
27049                     },
27050                     tabIndex:-1
27051                 });
27052             }
27053             cmenu.menu.items.push({
27054                 actiontype : 'word',
27055                 html: 'Remove MS Word Formating',
27056                 handler: function(a,b) {
27057                     editorcore.cleanWord();
27058                     editorcore.syncValue();
27059                 },
27060                 tabIndex:-1
27061             });
27062             
27063             cmenu.menu.items.push({
27064                 actiontype : 'all',
27065                 html: 'Remove All Styles',
27066                 handler: function(a,b) {
27067                     
27068                     var c = Roo.get(editorcore.doc.body);
27069                     c.select('[style]').each(function(s) {
27070                         s.dom.removeAttribute('style');
27071                     });
27072                     editorcore.syncValue();
27073                 },
27074                 tabIndex:-1
27075             });
27076              cmenu.menu.items.push({
27077                 actiontype : 'word',
27078                 html: 'Tidy HTML Source',
27079                 handler: function(a,b) {
27080                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27081                     editorcore.syncValue();
27082                 },
27083                 tabIndex:-1
27084             });
27085             
27086             
27087             tb.add(cmenu);
27088         }
27089          
27090         if (!this.disable.specialElements) {
27091             var semenu = {
27092                 text: "Other;",
27093                 cls: 'x-edit-none',
27094                 menu : {
27095                     items : []
27096                 }
27097             };
27098             for (var i =0; i < this.specialElements.length; i++) {
27099                 semenu.menu.items.push(
27100                     Roo.apply({ 
27101                         handler: function(a,b) {
27102                             editor.insertAtCursor(this.ihtml);
27103                         }
27104                     }, this.specialElements[i])
27105                 );
27106                     
27107             }
27108             
27109             tb.add(semenu);
27110             
27111             
27112         }
27113          
27114         
27115         if (this.btns) {
27116             for(var i =0; i< this.btns.length;i++) {
27117                 var b = Roo.factory(this.btns[i],Roo.form);
27118                 b.cls =  'x-edit-none';
27119                 b.scope = editorcore;
27120                 tb.add(b);
27121             }
27122         
27123         }
27124         
27125         
27126         
27127         // disable everything...
27128         
27129         this.tb.items.each(function(item){
27130            if(item.id != editorcore.frameId+ '-sourceedit'){
27131                 item.disable();
27132             }
27133         });
27134         this.rendered = true;
27135         
27136         // the all the btns;
27137         editor.on('editorevent', this.updateToolbar, this);
27138         // other toolbars need to implement this..
27139         //editor.on('editmodechange', this.updateToolbar, this);
27140     },
27141     
27142     
27143     relayBtnCmd : function(btn) {
27144         this.editorcore.relayCmd(btn.cmd);
27145     },
27146     // private used internally
27147     createLink : function(){
27148         Roo.log("create link?");
27149         var url = prompt(this.createLinkText, this.defaultLinkValue);
27150         if(url && url != 'http:/'+'/'){
27151             this.editorcore.relayCmd('createlink', url);
27152         }
27153     },
27154
27155     
27156     /**
27157      * Protected method that will not generally be called directly. It triggers
27158      * a toolbar update by reading the markup state of the current selection in the editor.
27159      */
27160     updateToolbar: function(){
27161
27162         if(!this.editorcore.activated){
27163             this.editor.onFirstFocus();
27164             return;
27165         }
27166
27167         var btns = this.tb.items.map, 
27168             doc = this.editorcore.doc,
27169             frameId = this.editorcore.frameId;
27170
27171         if(!this.disable.font && !Roo.isSafari){
27172             /*
27173             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27174             if(name != this.fontSelect.dom.value){
27175                 this.fontSelect.dom.value = name;
27176             }
27177             */
27178         }
27179         if(!this.disable.format){
27180             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27181             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27182             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27183         }
27184         if(!this.disable.alignments){
27185             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27186             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27187             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27188         }
27189         if(!Roo.isSafari && !this.disable.lists){
27190             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27191             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27192         }
27193         
27194         var ans = this.editorcore.getAllAncestors();
27195         if (this.formatCombo) {
27196             
27197             
27198             var store = this.formatCombo.store;
27199             this.formatCombo.setValue("");
27200             for (var i =0; i < ans.length;i++) {
27201                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27202                     // select it..
27203                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27204                     break;
27205                 }
27206             }
27207         }
27208         
27209         
27210         
27211         // hides menus... - so this cant be on a menu...
27212         Roo.menu.MenuMgr.hideAll();
27213
27214         //this.editorsyncValue();
27215     },
27216    
27217     
27218     createFontOptions : function(){
27219         var buf = [], fs = this.fontFamilies, ff, lc;
27220         
27221         
27222         
27223         for(var i = 0, len = fs.length; i< len; i++){
27224             ff = fs[i];
27225             lc = ff.toLowerCase();
27226             buf.push(
27227                 '<option value="',lc,'" style="font-family:',ff,';"',
27228                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27229                     ff,
27230                 '</option>'
27231             );
27232         }
27233         return buf.join('');
27234     },
27235     
27236     toggleSourceEdit : function(sourceEditMode){
27237         
27238         Roo.log("toolbar toogle");
27239         if(sourceEditMode === undefined){
27240             sourceEditMode = !this.sourceEditMode;
27241         }
27242         this.sourceEditMode = sourceEditMode === true;
27243         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27244         // just toggle the button?
27245         if(btn.pressed !== this.sourceEditMode){
27246             btn.toggle(this.sourceEditMode);
27247             return;
27248         }
27249         
27250         if(sourceEditMode){
27251             Roo.log("disabling buttons");
27252             this.tb.items.each(function(item){
27253                 if(item.cmd != 'sourceedit'){
27254                     item.disable();
27255                 }
27256             });
27257           
27258         }else{
27259             Roo.log("enabling buttons");
27260             if(this.editorcore.initialized){
27261                 this.tb.items.each(function(item){
27262                     item.enable();
27263                 });
27264             }
27265             
27266         }
27267         Roo.log("calling toggole on editor");
27268         // tell the editor that it's been pressed..
27269         this.editor.toggleSourceEdit(sourceEditMode);
27270        
27271     },
27272      /**
27273      * Object collection of toolbar tooltips for the buttons in the editor. The key
27274      * is the command id associated with that button and the value is a valid QuickTips object.
27275      * For example:
27276 <pre><code>
27277 {
27278     bold : {
27279         title: 'Bold (Ctrl+B)',
27280         text: 'Make the selected text bold.',
27281         cls: 'x-html-editor-tip'
27282     },
27283     italic : {
27284         title: 'Italic (Ctrl+I)',
27285         text: 'Make the selected text italic.',
27286         cls: 'x-html-editor-tip'
27287     },
27288     ...
27289 </code></pre>
27290     * @type Object
27291      */
27292     buttonTips : {
27293         bold : {
27294             title: 'Bold (Ctrl+B)',
27295             text: 'Make the selected text bold.',
27296             cls: 'x-html-editor-tip'
27297         },
27298         italic : {
27299             title: 'Italic (Ctrl+I)',
27300             text: 'Make the selected text italic.',
27301             cls: 'x-html-editor-tip'
27302         },
27303         underline : {
27304             title: 'Underline (Ctrl+U)',
27305             text: 'Underline the selected text.',
27306             cls: 'x-html-editor-tip'
27307         },
27308         increasefontsize : {
27309             title: 'Grow Text',
27310             text: 'Increase the font size.',
27311             cls: 'x-html-editor-tip'
27312         },
27313         decreasefontsize : {
27314             title: 'Shrink Text',
27315             text: 'Decrease the font size.',
27316             cls: 'x-html-editor-tip'
27317         },
27318         backcolor : {
27319             title: 'Text Highlight Color',
27320             text: 'Change the background color of the selected text.',
27321             cls: 'x-html-editor-tip'
27322         },
27323         forecolor : {
27324             title: 'Font Color',
27325             text: 'Change the color of the selected text.',
27326             cls: 'x-html-editor-tip'
27327         },
27328         justifyleft : {
27329             title: 'Align Text Left',
27330             text: 'Align text to the left.',
27331             cls: 'x-html-editor-tip'
27332         },
27333         justifycenter : {
27334             title: 'Center Text',
27335             text: 'Center text in the editor.',
27336             cls: 'x-html-editor-tip'
27337         },
27338         justifyright : {
27339             title: 'Align Text Right',
27340             text: 'Align text to the right.',
27341             cls: 'x-html-editor-tip'
27342         },
27343         insertunorderedlist : {
27344             title: 'Bullet List',
27345             text: 'Start a bulleted list.',
27346             cls: 'x-html-editor-tip'
27347         },
27348         insertorderedlist : {
27349             title: 'Numbered List',
27350             text: 'Start a numbered list.',
27351             cls: 'x-html-editor-tip'
27352         },
27353         createlink : {
27354             title: 'Hyperlink',
27355             text: 'Make the selected text a hyperlink.',
27356             cls: 'x-html-editor-tip'
27357         },
27358         sourceedit : {
27359             title: 'Source Edit',
27360             text: 'Switch to source editing mode.',
27361             cls: 'x-html-editor-tip'
27362         }
27363     },
27364     // private
27365     onDestroy : function(){
27366         if(this.rendered){
27367             
27368             this.tb.items.each(function(item){
27369                 if(item.menu){
27370                     item.menu.removeAll();
27371                     if(item.menu.el){
27372                         item.menu.el.destroy();
27373                     }
27374                 }
27375                 item.destroy();
27376             });
27377              
27378         }
27379     },
27380     onFirstFocus: function() {
27381         this.tb.items.each(function(item){
27382            item.enable();
27383         });
27384     }
27385 });
27386
27387
27388
27389
27390 // <script type="text/javascript">
27391 /*
27392  * Based on
27393  * Ext JS Library 1.1.1
27394  * Copyright(c) 2006-2007, Ext JS, LLC.
27395  *  
27396  
27397  */
27398
27399  
27400 /**
27401  * @class Roo.form.HtmlEditor.ToolbarContext
27402  * Context Toolbar
27403  * 
27404  * Usage:
27405  *
27406  new Roo.form.HtmlEditor({
27407     ....
27408     toolbars : [
27409         { xtype: 'ToolbarStandard', styles : {} }
27410         { xtype: 'ToolbarContext', disable : {} }
27411     ]
27412 })
27413
27414      
27415  * 
27416  * @config : {Object} disable List of elements to disable.. (not done yet.)
27417  * @config : {Object} styles  Map of styles available.
27418  * 
27419  */
27420
27421 Roo.form.HtmlEditor.ToolbarContext = function(config)
27422 {
27423     
27424     Roo.apply(this, config);
27425     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27426     // dont call parent... till later.
27427     this.styles = this.styles || {};
27428 }
27429
27430  
27431
27432 Roo.form.HtmlEditor.ToolbarContext.types = {
27433     'IMG' : {
27434         width : {
27435             title: "Width",
27436             width: 40
27437         },
27438         height:  {
27439             title: "Height",
27440             width: 40
27441         },
27442         align: {
27443             title: "Align",
27444             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27445             width : 80
27446             
27447         },
27448         border: {
27449             title: "Border",
27450             width: 40
27451         },
27452         alt: {
27453             title: "Alt",
27454             width: 120
27455         },
27456         src : {
27457             title: "Src",
27458             width: 220
27459         }
27460         
27461     },
27462     'A' : {
27463         name : {
27464             title: "Name",
27465             width: 50
27466         },
27467         target:  {
27468             title: "Target",
27469             width: 120
27470         },
27471         href:  {
27472             title: "Href",
27473             width: 220
27474         } // border?
27475         
27476     },
27477     'TABLE' : {
27478         rows : {
27479             title: "Rows",
27480             width: 20
27481         },
27482         cols : {
27483             title: "Cols",
27484             width: 20
27485         },
27486         width : {
27487             title: "Width",
27488             width: 40
27489         },
27490         height : {
27491             title: "Height",
27492             width: 40
27493         },
27494         border : {
27495             title: "Border",
27496             width: 20
27497         }
27498     },
27499     'TD' : {
27500         width : {
27501             title: "Width",
27502             width: 40
27503         },
27504         height : {
27505             title: "Height",
27506             width: 40
27507         },   
27508         align: {
27509             title: "Align",
27510             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27511             width: 80
27512         },
27513         valign: {
27514             title: "Valign",
27515             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27516             width: 80
27517         },
27518         colspan: {
27519             title: "Colspan",
27520             width: 20
27521             
27522         },
27523          'font-family'  : {
27524             title : "Font",
27525             style : 'fontFamily',
27526             displayField: 'display',
27527             optname : 'font-family',
27528             width: 140
27529         }
27530     },
27531     'INPUT' : {
27532         name : {
27533             title: "name",
27534             width: 120
27535         },
27536         value : {
27537             title: "Value",
27538             width: 120
27539         },
27540         width : {
27541             title: "Width",
27542             width: 40
27543         }
27544     },
27545     'LABEL' : {
27546         'for' : {
27547             title: "For",
27548             width: 120
27549         }
27550     },
27551     'TEXTAREA' : {
27552           name : {
27553             title: "name",
27554             width: 120
27555         },
27556         rows : {
27557             title: "Rows",
27558             width: 20
27559         },
27560         cols : {
27561             title: "Cols",
27562             width: 20
27563         }
27564     },
27565     'SELECT' : {
27566         name : {
27567             title: "name",
27568             width: 120
27569         },
27570         selectoptions : {
27571             title: "Options",
27572             width: 200
27573         }
27574     },
27575     
27576     // should we really allow this??
27577     // should this just be 
27578     'BODY' : {
27579         title : {
27580             title: "Title",
27581             width: 200,
27582             disabled : true
27583         }
27584     },
27585     'SPAN' : {
27586         'font-family'  : {
27587             title : "Font",
27588             style : 'fontFamily',
27589             displayField: 'display',
27590             optname : 'font-family',
27591             width: 140
27592         }
27593     },
27594     'DIV' : {
27595         'font-family'  : {
27596             title : "Font",
27597             style : 'fontFamily',
27598             displayField: 'display',
27599             optname : 'font-family',
27600             width: 140
27601         }
27602     },
27603      'P' : {
27604         'font-family'  : {
27605             title : "Font",
27606             style : 'fontFamily',
27607             displayField: 'display',
27608             optname : 'font-family',
27609             width: 140
27610         }
27611     },
27612     
27613     '*' : {
27614         // empty..
27615     }
27616
27617 };
27618
27619 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27620 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27621
27622 Roo.form.HtmlEditor.ToolbarContext.options = {
27623         'font-family'  : [ 
27624                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27625                 [ 'Courier New', 'Courier New'],
27626                 [ 'Tahoma', 'Tahoma'],
27627                 [ 'Times New Roman,serif', 'Times'],
27628                 [ 'Verdana','Verdana' ]
27629         ]
27630 };
27631
27632 // fixme - these need to be configurable..
27633  
27634
27635 Roo.form.HtmlEditor.ToolbarContext.types
27636
27637
27638 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27639     
27640     tb: false,
27641     
27642     rendered: false,
27643     
27644     editor : false,
27645     editorcore : false,
27646     /**
27647      * @cfg {Object} disable  List of toolbar elements to disable
27648          
27649      */
27650     disable : false,
27651     /**
27652      * @cfg {Object} styles List of styles 
27653      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27654      *
27655      * These must be defined in the page, so they get rendered correctly..
27656      * .headline { }
27657      * TD.underline { }
27658      * 
27659      */
27660     styles : false,
27661     
27662     options: false,
27663     
27664     toolbars : false,
27665     
27666     init : function(editor)
27667     {
27668         this.editor = editor;
27669         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27670         var editorcore = this.editorcore;
27671         
27672         var fid = editorcore.frameId;
27673         var etb = this;
27674         function btn(id, toggle, handler){
27675             var xid = fid + '-'+ id ;
27676             return {
27677                 id : xid,
27678                 cmd : id,
27679                 cls : 'x-btn-icon x-edit-'+id,
27680                 enableToggle:toggle !== false,
27681                 scope: editorcore, // was editor...
27682                 handler:handler||editorcore.relayBtnCmd,
27683                 clickEvent:'mousedown',
27684                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27685                 tabIndex:-1
27686             };
27687         }
27688         // create a new element.
27689         var wdiv = editor.wrap.createChild({
27690                 tag: 'div'
27691             }, editor.wrap.dom.firstChild.nextSibling, true);
27692         
27693         // can we do this more than once??
27694         
27695          // stop form submits
27696       
27697  
27698         // disable everything...
27699         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27700         this.toolbars = {};
27701            
27702         for (var i in  ty) {
27703           
27704             this.toolbars[i] = this.buildToolbar(ty[i],i);
27705         }
27706         this.tb = this.toolbars.BODY;
27707         this.tb.el.show();
27708         this.buildFooter();
27709         this.footer.show();
27710         editor.on('hide', function( ) { this.footer.hide() }, this);
27711         editor.on('show', function( ) { this.footer.show() }, this);
27712         
27713          
27714         this.rendered = true;
27715         
27716         // the all the btns;
27717         editor.on('editorevent', this.updateToolbar, this);
27718         // other toolbars need to implement this..
27719         //editor.on('editmodechange', this.updateToolbar, this);
27720     },
27721     
27722     
27723     
27724     /**
27725      * Protected method that will not generally be called directly. It triggers
27726      * a toolbar update by reading the markup state of the current selection in the editor.
27727      */
27728     updateToolbar: function(editor,ev,sel){
27729
27730         //Roo.log(ev);
27731         // capture mouse up - this is handy for selecting images..
27732         // perhaps should go somewhere else...
27733         if(!this.editorcore.activated){
27734              this.editor.onFirstFocus();
27735             return;
27736         }
27737         
27738         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27739         // selectNode - might want to handle IE?
27740         if (ev &&
27741             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27742             ev.target && ev.target.tagName == 'IMG') {
27743             // they have click on an image...
27744             // let's see if we can change the selection...
27745             sel = ev.target;
27746          
27747               var nodeRange = sel.ownerDocument.createRange();
27748             try {
27749                 nodeRange.selectNode(sel);
27750             } catch (e) {
27751                 nodeRange.selectNodeContents(sel);
27752             }
27753             //nodeRange.collapse(true);
27754             var s = this.editorcore.win.getSelection();
27755             s.removeAllRanges();
27756             s.addRange(nodeRange);
27757         }  
27758         
27759       
27760         var updateFooter = sel ? false : true;
27761         
27762         
27763         var ans = this.editorcore.getAllAncestors();
27764         
27765         // pick
27766         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27767         
27768         if (!sel) { 
27769             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
27770             sel = sel ? sel : this.editorcore.doc.body;
27771             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
27772             
27773         }
27774         // pick a menu that exists..
27775         var tn = sel.tagName.toUpperCase();
27776         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27777         
27778         tn = sel.tagName.toUpperCase();
27779         
27780         var lastSel = this.tb.selectedNode
27781         
27782         this.tb.selectedNode = sel;
27783         
27784         // if current menu does not match..
27785         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27786                 
27787             this.tb.el.hide();
27788             ///console.log("show: " + tn);
27789             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27790             this.tb.el.show();
27791             // update name
27792             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27793             
27794             
27795             // update attributes
27796             if (this.tb.fields) {
27797                 this.tb.fields.each(function(e) {
27798                     if (e.stylename) {
27799                         e.setValue(sel.style[e.stylename]);
27800                         return;
27801                     } 
27802                    e.setValue(sel.getAttribute(e.attrname));
27803                 });
27804             }
27805             
27806             var hasStyles = false;
27807             for(var i in this.styles) {
27808                 hasStyles = true;
27809                 break;
27810             }
27811             
27812             // update styles
27813             if (hasStyles) { 
27814                 var st = this.tb.fields.item(0);
27815                 
27816                 st.store.removeAll();
27817                
27818                 
27819                 var cn = sel.className.split(/\s+/);
27820                 
27821                 var avs = [];
27822                 if (this.styles['*']) {
27823                     
27824                     Roo.each(this.styles['*'], function(v) {
27825                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27826                     });
27827                 }
27828                 if (this.styles[tn]) { 
27829                     Roo.each(this.styles[tn], function(v) {
27830                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27831                     });
27832                 }
27833                 
27834                 st.store.loadData(avs);
27835                 st.collapse();
27836                 st.setValue(cn);
27837             }
27838             // flag our selected Node.
27839             this.tb.selectedNode = sel;
27840            
27841            
27842             Roo.menu.MenuMgr.hideAll();
27843
27844         }
27845         
27846         if (!updateFooter) {
27847             //this.footDisp.dom.innerHTML = ''; 
27848             return;
27849         }
27850         // update the footer
27851         //
27852         var html = '';
27853         
27854         this.footerEls = ans.reverse();
27855         Roo.each(this.footerEls, function(a,i) {
27856             if (!a) { return; }
27857             html += html.length ? ' &gt; '  :  '';
27858             
27859             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27860             
27861         });
27862        
27863         // 
27864         var sz = this.footDisp.up('td').getSize();
27865         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27866         this.footDisp.dom.style.marginLeft = '5px';
27867         
27868         this.footDisp.dom.style.overflow = 'hidden';
27869         
27870         this.footDisp.dom.innerHTML = html;
27871             
27872         //this.editorsyncValue();
27873     },
27874      
27875     
27876    
27877        
27878     // private
27879     onDestroy : function(){
27880         if(this.rendered){
27881             
27882             this.tb.items.each(function(item){
27883                 if(item.menu){
27884                     item.menu.removeAll();
27885                     if(item.menu.el){
27886                         item.menu.el.destroy();
27887                     }
27888                 }
27889                 item.destroy();
27890             });
27891              
27892         }
27893     },
27894     onFirstFocus: function() {
27895         // need to do this for all the toolbars..
27896         this.tb.items.each(function(item){
27897            item.enable();
27898         });
27899     },
27900     buildToolbar: function(tlist, nm)
27901     {
27902         var editor = this.editor;
27903         var editorcore = this.editorcore;
27904          // create a new element.
27905         var wdiv = editor.wrap.createChild({
27906                 tag: 'div'
27907             }, editor.wrap.dom.firstChild.nextSibling, true);
27908         
27909        
27910         var tb = new Roo.Toolbar(wdiv);
27911         // add the name..
27912         
27913         tb.add(nm+ ":&nbsp;");
27914         
27915         var styles = [];
27916         for(var i in this.styles) {
27917             styles.push(i);
27918         }
27919         
27920         // styles...
27921         if (styles && styles.length) {
27922             
27923             // this needs a multi-select checkbox...
27924             tb.addField( new Roo.form.ComboBox({
27925                 store: new Roo.data.SimpleStore({
27926                     id : 'val',
27927                     fields: ['val', 'selected'],
27928                     data : [] 
27929                 }),
27930                 name : '-roo-edit-className',
27931                 attrname : 'className',
27932                 displayField: 'val',
27933                 typeAhead: false,
27934                 mode: 'local',
27935                 editable : false,
27936                 triggerAction: 'all',
27937                 emptyText:'Select Style',
27938                 selectOnFocus:true,
27939                 width: 130,
27940                 listeners : {
27941                     'select': function(c, r, i) {
27942                         // initial support only for on class per el..
27943                         tb.selectedNode.className =  r ? r.get('val') : '';
27944                         editorcore.syncValue();
27945                     }
27946                 }
27947     
27948             }));
27949         }
27950         
27951         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27952         var tbops = tbc.options;
27953         
27954         for (var i in tlist) {
27955             
27956             var item = tlist[i];
27957             tb.add(item.title + ":&nbsp;");
27958             
27959             
27960             //optname == used so you can configure the options available..
27961             var opts = item.opts ? item.opts : false;
27962             if (item.optname) {
27963                 opts = tbops[item.optname];
27964            
27965             }
27966             
27967             if (opts) {
27968                 // opts == pulldown..
27969                 tb.addField( new Roo.form.ComboBox({
27970                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27971                         id : 'val',
27972                         fields: ['val', 'display'],
27973                         data : opts  
27974                     }),
27975                     name : '-roo-edit-' + i,
27976                     attrname : i,
27977                     stylename : item.style ? item.style : false,
27978                     displayField: item.displayField ? item.displayField : 'val',
27979                     valueField :  'val',
27980                     typeAhead: false,
27981                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27982                     editable : false,
27983                     triggerAction: 'all',
27984                     emptyText:'Select',
27985                     selectOnFocus:true,
27986                     width: item.width ? item.width  : 130,
27987                     listeners : {
27988                         'select': function(c, r, i) {
27989                             if (c.stylename) {
27990                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27991                                 return;
27992                             }
27993                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27994                         }
27995                     }
27996
27997                 }));
27998                 continue;
27999                     
28000                  
28001                 
28002                 tb.addField( new Roo.form.TextField({
28003                     name: i,
28004                     width: 100,
28005                     //allowBlank:false,
28006                     value: ''
28007                 }));
28008                 continue;
28009             }
28010             tb.addField( new Roo.form.TextField({
28011                 name: '-roo-edit-' + i,
28012                 attrname : i,
28013                 
28014                 width: item.width,
28015                 //allowBlank:true,
28016                 value: '',
28017                 listeners: {
28018                     'change' : function(f, nv, ov) {
28019                         tb.selectedNode.setAttribute(f.attrname, nv);
28020                     }
28021                 }
28022             }));
28023              
28024         }
28025         tb.addFill();
28026         var _this = this;
28027         tb.addButton( {
28028             text: 'Remove Tag',
28029     
28030             listeners : {
28031                 click : function ()
28032                 {
28033                     // remove
28034                     // undo does not work.
28035                      
28036                     var sn = tb.selectedNode;
28037                     
28038                     var pn = sn.parentNode;
28039                     
28040                     var stn =  sn.childNodes[0];
28041                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28042                     while (sn.childNodes.length) {
28043                         var node = sn.childNodes[0];
28044                         sn.removeChild(node);
28045                         //Roo.log(node);
28046                         pn.insertBefore(node, sn);
28047                         
28048                     }
28049                     pn.removeChild(sn);
28050                     var range = editorcore.createRange();
28051         
28052                     range.setStart(stn,0);
28053                     range.setEnd(en,0); //????
28054                     //range.selectNode(sel);
28055                     
28056                     
28057                     var selection = editorcore.getSelection();
28058                     selection.removeAllRanges();
28059                     selection.addRange(range);
28060                     
28061                     
28062                     
28063                     //_this.updateToolbar(null, null, pn);
28064                     _this.updateToolbar(null, null, null);
28065                     _this.footDisp.dom.innerHTML = ''; 
28066                 }
28067             }
28068             
28069                     
28070                 
28071             
28072         });
28073         
28074         
28075         tb.el.on('click', function(e){
28076             e.preventDefault(); // what does this do?
28077         });
28078         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28079         tb.el.hide();
28080         tb.name = nm;
28081         // dont need to disable them... as they will get hidden
28082         return tb;
28083          
28084         
28085     },
28086     buildFooter : function()
28087     {
28088         
28089         var fel = this.editor.wrap.createChild();
28090         this.footer = new Roo.Toolbar(fel);
28091         // toolbar has scrolly on left / right?
28092         var footDisp= new Roo.Toolbar.Fill();
28093         var _t = this;
28094         this.footer.add(
28095             {
28096                 text : '&lt;',
28097                 xtype: 'Button',
28098                 handler : function() {
28099                     _t.footDisp.scrollTo('left',0,true)
28100                 }
28101             }
28102         );
28103         this.footer.add( footDisp );
28104         this.footer.add( 
28105             {
28106                 text : '&gt;',
28107                 xtype: 'Button',
28108                 handler : function() {
28109                     // no animation..
28110                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28111                 }
28112             }
28113         );
28114         var fel = Roo.get(footDisp.el);
28115         fel.addClass('x-editor-context');
28116         this.footDispWrap = fel; 
28117         this.footDispWrap.overflow  = 'hidden';
28118         
28119         this.footDisp = fel.createChild();
28120         this.footDispWrap.on('click', this.onContextClick, this)
28121         
28122         
28123     },
28124     onContextClick : function (ev,dom)
28125     {
28126         ev.preventDefault();
28127         var  cn = dom.className;
28128         //Roo.log(cn);
28129         if (!cn.match(/x-ed-loc-/)) {
28130             return;
28131         }
28132         var n = cn.split('-').pop();
28133         var ans = this.footerEls;
28134         var sel = ans[n];
28135         
28136          // pick
28137         var range = this.editorcore.createRange();
28138         
28139         range.selectNodeContents(sel);
28140         //range.selectNode(sel);
28141         
28142         
28143         var selection = this.editorcore.getSelection();
28144         selection.removeAllRanges();
28145         selection.addRange(range);
28146         
28147         
28148         
28149         this.updateToolbar(null, null, sel);
28150         
28151         
28152     }
28153     
28154     
28155     
28156     
28157     
28158 });
28159
28160
28161
28162
28163
28164 /*
28165  * Based on:
28166  * Ext JS Library 1.1.1
28167  * Copyright(c) 2006-2007, Ext JS, LLC.
28168  *
28169  * Originally Released Under LGPL - original licence link has changed is not relivant.
28170  *
28171  * Fork - LGPL
28172  * <script type="text/javascript">
28173  */
28174  
28175 /**
28176  * @class Roo.form.BasicForm
28177  * @extends Roo.util.Observable
28178  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28179  * @constructor
28180  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28181  * @param {Object} config Configuration options
28182  */
28183 Roo.form.BasicForm = function(el, config){
28184     this.allItems = [];
28185     this.childForms = [];
28186     Roo.apply(this, config);
28187     /*
28188      * The Roo.form.Field items in this form.
28189      * @type MixedCollection
28190      */
28191      
28192      
28193     this.items = new Roo.util.MixedCollection(false, function(o){
28194         return o.id || (o.id = Roo.id());
28195     });
28196     this.addEvents({
28197         /**
28198          * @event beforeaction
28199          * Fires before any action is performed. Return false to cancel the action.
28200          * @param {Form} this
28201          * @param {Action} action The action to be performed
28202          */
28203         beforeaction: true,
28204         /**
28205          * @event actionfailed
28206          * Fires when an action fails.
28207          * @param {Form} this
28208          * @param {Action} action The action that failed
28209          */
28210         actionfailed : true,
28211         /**
28212          * @event actioncomplete
28213          * Fires when an action is completed.
28214          * @param {Form} this
28215          * @param {Action} action The action that completed
28216          */
28217         actioncomplete : true
28218     });
28219     if(el){
28220         this.initEl(el);
28221     }
28222     Roo.form.BasicForm.superclass.constructor.call(this);
28223 };
28224
28225 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28226     /**
28227      * @cfg {String} method
28228      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28229      */
28230     /**
28231      * @cfg {DataReader} reader
28232      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28233      * This is optional as there is built-in support for processing JSON.
28234      */
28235     /**
28236      * @cfg {DataReader} errorReader
28237      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28238      * This is completely optional as there is built-in support for processing JSON.
28239      */
28240     /**
28241      * @cfg {String} url
28242      * The URL to use for form actions if one isn't supplied in the action options.
28243      */
28244     /**
28245      * @cfg {Boolean} fileUpload
28246      * Set to true if this form is a file upload.
28247      */
28248      
28249     /**
28250      * @cfg {Object} baseParams
28251      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28252      */
28253      /**
28254      
28255     /**
28256      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28257      */
28258     timeout: 30,
28259
28260     // private
28261     activeAction : null,
28262
28263     /**
28264      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28265      * or setValues() data instead of when the form was first created.
28266      */
28267     trackResetOnLoad : false,
28268     
28269     
28270     /**
28271      * childForms - used for multi-tab forms
28272      * @type {Array}
28273      */
28274     childForms : false,
28275     
28276     /**
28277      * allItems - full list of fields.
28278      * @type {Array}
28279      */
28280     allItems : false,
28281     
28282     /**
28283      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28284      * element by passing it or its id or mask the form itself by passing in true.
28285      * @type Mixed
28286      */
28287     waitMsgTarget : false,
28288
28289     // private
28290     initEl : function(el){
28291         this.el = Roo.get(el);
28292         this.id = this.el.id || Roo.id();
28293         this.el.on('submit', this.onSubmit, this);
28294         this.el.addClass('x-form');
28295     },
28296
28297     // private
28298     onSubmit : function(e){
28299         e.stopEvent();
28300     },
28301
28302     /**
28303      * Returns true if client-side validation on the form is successful.
28304      * @return Boolean
28305      */
28306     isValid : function(){
28307         var valid = true;
28308         this.items.each(function(f){
28309            if(!f.validate()){
28310                valid = false;
28311            }
28312         });
28313         return valid;
28314     },
28315
28316     /**
28317      * Returns true if any fields in this form have changed since their original load.
28318      * @return Boolean
28319      */
28320     isDirty : function(){
28321         var dirty = false;
28322         this.items.each(function(f){
28323            if(f.isDirty()){
28324                dirty = true;
28325                return false;
28326            }
28327         });
28328         return dirty;
28329     },
28330
28331     /**
28332      * Performs a predefined action (submit or load) or custom actions you define on this form.
28333      * @param {String} actionName The name of the action type
28334      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28335      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28336      * accept other config options):
28337      * <pre>
28338 Property          Type             Description
28339 ----------------  ---------------  ----------------------------------------------------------------------------------
28340 url               String           The url for the action (defaults to the form's url)
28341 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28342 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28343 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28344                                    validate the form on the client (defaults to false)
28345      * </pre>
28346      * @return {BasicForm} this
28347      */
28348     doAction : function(action, options){
28349         if(typeof action == 'string'){
28350             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28351         }
28352         if(this.fireEvent('beforeaction', this, action) !== false){
28353             this.beforeAction(action);
28354             action.run.defer(100, action);
28355         }
28356         return this;
28357     },
28358
28359     /**
28360      * Shortcut to do a submit action.
28361      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28362      * @return {BasicForm} this
28363      */
28364     submit : function(options){
28365         this.doAction('submit', options);
28366         return this;
28367     },
28368
28369     /**
28370      * Shortcut to do a load action.
28371      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28372      * @return {BasicForm} this
28373      */
28374     load : function(options){
28375         this.doAction('load', options);
28376         return this;
28377     },
28378
28379     /**
28380      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28381      * @param {Record} record The record to edit
28382      * @return {BasicForm} this
28383      */
28384     updateRecord : function(record){
28385         record.beginEdit();
28386         var fs = record.fields;
28387         fs.each(function(f){
28388             var field = this.findField(f.name);
28389             if(field){
28390                 record.set(f.name, field.getValue());
28391             }
28392         }, this);
28393         record.endEdit();
28394         return this;
28395     },
28396
28397     /**
28398      * Loads an Roo.data.Record into this form.
28399      * @param {Record} record The record to load
28400      * @return {BasicForm} this
28401      */
28402     loadRecord : function(record){
28403         this.setValues(record.data);
28404         return this;
28405     },
28406
28407     // private
28408     beforeAction : function(action){
28409         var o = action.options;
28410         
28411        
28412         if(this.waitMsgTarget === true){
28413             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28414         }else if(this.waitMsgTarget){
28415             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28416             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28417         }else {
28418             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28419         }
28420          
28421     },
28422
28423     // private
28424     afterAction : function(action, success){
28425         this.activeAction = null;
28426         var o = action.options;
28427         
28428         if(this.waitMsgTarget === true){
28429             this.el.unmask();
28430         }else if(this.waitMsgTarget){
28431             this.waitMsgTarget.unmask();
28432         }else{
28433             Roo.MessageBox.updateProgress(1);
28434             Roo.MessageBox.hide();
28435         }
28436          
28437         if(success){
28438             if(o.reset){
28439                 this.reset();
28440             }
28441             Roo.callback(o.success, o.scope, [this, action]);
28442             this.fireEvent('actioncomplete', this, action);
28443             
28444         }else{
28445             
28446             // failure condition..
28447             // we have a scenario where updates need confirming.
28448             // eg. if a locking scenario exists..
28449             // we look for { errors : { needs_confirm : true }} in the response.
28450             if (
28451                 (typeof(action.result) != 'undefined')  &&
28452                 (typeof(action.result.errors) != 'undefined')  &&
28453                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28454            ){
28455                 var _t = this;
28456                 Roo.MessageBox.confirm(
28457                     "Change requires confirmation",
28458                     action.result.errorMsg,
28459                     function(r) {
28460                         if (r != 'yes') {
28461                             return;
28462                         }
28463                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28464                     }
28465                     
28466                 );
28467                 
28468                 
28469                 
28470                 return;
28471             }
28472             
28473             Roo.callback(o.failure, o.scope, [this, action]);
28474             // show an error message if no failed handler is set..
28475             if (!this.hasListener('actionfailed')) {
28476                 Roo.MessageBox.alert("Error",
28477                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28478                         action.result.errorMsg :
28479                         "Saving Failed, please check your entries or try again"
28480                 );
28481             }
28482             
28483             this.fireEvent('actionfailed', this, action);
28484         }
28485         
28486     },
28487
28488     /**
28489      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28490      * @param {String} id The value to search for
28491      * @return Field
28492      */
28493     findField : function(id){
28494         var field = this.items.get(id);
28495         if(!field){
28496             this.items.each(function(f){
28497                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28498                     field = f;
28499                     return false;
28500                 }
28501             });
28502         }
28503         return field || null;
28504     },
28505
28506     /**
28507      * Add a secondary form to this one, 
28508      * Used to provide tabbed forms. One form is primary, with hidden values 
28509      * which mirror the elements from the other forms.
28510      * 
28511      * @param {Roo.form.Form} form to add.
28512      * 
28513      */
28514     addForm : function(form)
28515     {
28516        
28517         if (this.childForms.indexOf(form) > -1) {
28518             // already added..
28519             return;
28520         }
28521         this.childForms.push(form);
28522         var n = '';
28523         Roo.each(form.allItems, function (fe) {
28524             
28525             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28526             if (this.findField(n)) { // already added..
28527                 return;
28528             }
28529             var add = new Roo.form.Hidden({
28530                 name : n
28531             });
28532             add.render(this.el);
28533             
28534             this.add( add );
28535         }, this);
28536         
28537     },
28538     /**
28539      * Mark fields in this form invalid in bulk.
28540      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28541      * @return {BasicForm} this
28542      */
28543     markInvalid : function(errors){
28544         if(errors instanceof Array){
28545             for(var i = 0, len = errors.length; i < len; i++){
28546                 var fieldError = errors[i];
28547                 var f = this.findField(fieldError.id);
28548                 if(f){
28549                     f.markInvalid(fieldError.msg);
28550                 }
28551             }
28552         }else{
28553             var field, id;
28554             for(id in errors){
28555                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28556                     field.markInvalid(errors[id]);
28557                 }
28558             }
28559         }
28560         Roo.each(this.childForms || [], function (f) {
28561             f.markInvalid(errors);
28562         });
28563         
28564         return this;
28565     },
28566
28567     /**
28568      * Set values for fields in this form in bulk.
28569      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28570      * @return {BasicForm} this
28571      */
28572     setValues : function(values){
28573         if(values instanceof Array){ // array of objects
28574             for(var i = 0, len = values.length; i < len; i++){
28575                 var v = values[i];
28576                 var f = this.findField(v.id);
28577                 if(f){
28578                     f.setValue(v.value);
28579                     if(this.trackResetOnLoad){
28580                         f.originalValue = f.getValue();
28581                     }
28582                 }
28583             }
28584         }else{ // object hash
28585             var field, id;
28586             for(id in values){
28587                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28588                     
28589                     if (field.setFromData && 
28590                         field.valueField && 
28591                         field.displayField &&
28592                         // combos' with local stores can 
28593                         // be queried via setValue()
28594                         // to set their value..
28595                         (field.store && !field.store.isLocal)
28596                         ) {
28597                         // it's a combo
28598                         var sd = { };
28599                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28600                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28601                         field.setFromData(sd);
28602                         
28603                     } else {
28604                         field.setValue(values[id]);
28605                     }
28606                     
28607                     
28608                     if(this.trackResetOnLoad){
28609                         field.originalValue = field.getValue();
28610                     }
28611                 }
28612             }
28613         }
28614          
28615         Roo.each(this.childForms || [], function (f) {
28616             f.setValues(values);
28617         });
28618                 
28619         return this;
28620     },
28621
28622     /**
28623      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28624      * they are returned as an array.
28625      * @param {Boolean} asString
28626      * @return {Object}
28627      */
28628     getValues : function(asString){
28629         if (this.childForms) {
28630             // copy values from the child forms
28631             Roo.each(this.childForms, function (f) {
28632                 this.setValues(f.getValues());
28633             }, this);
28634         }
28635         
28636         
28637         
28638         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28639         if(asString === true){
28640             return fs;
28641         }
28642         return Roo.urlDecode(fs);
28643     },
28644     
28645     /**
28646      * Returns the fields in this form as an object with key/value pairs. 
28647      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28648      * @return {Object}
28649      */
28650     getFieldValues : function(with_hidden)
28651     {
28652         if (this.childForms) {
28653             // copy values from the child forms
28654             // should this call getFieldValues - probably not as we do not currently copy
28655             // hidden fields when we generate..
28656             Roo.each(this.childForms, function (f) {
28657                 this.setValues(f.getValues());
28658             }, this);
28659         }
28660         
28661         var ret = {};
28662         this.items.each(function(f){
28663             if (!f.getName()) {
28664                 return;
28665             }
28666             var v = f.getValue();
28667             if (f.inputType =='radio') {
28668                 if (typeof(ret[f.getName()]) == 'undefined') {
28669                     ret[f.getName()] = ''; // empty..
28670                 }
28671                 
28672                 if (!f.el.dom.checked) {
28673                     return;
28674                     
28675                 }
28676                 v = f.el.dom.value;
28677                 
28678             }
28679             
28680             // not sure if this supported any more..
28681             if ((typeof(v) == 'object') && f.getRawValue) {
28682                 v = f.getRawValue() ; // dates..
28683             }
28684             // combo boxes where name != hiddenName...
28685             if (f.name != f.getName()) {
28686                 ret[f.name] = f.getRawValue();
28687             }
28688             ret[f.getName()] = v;
28689         });
28690         
28691         return ret;
28692     },
28693
28694     /**
28695      * Clears all invalid messages in this form.
28696      * @return {BasicForm} this
28697      */
28698     clearInvalid : function(){
28699         this.items.each(function(f){
28700            f.clearInvalid();
28701         });
28702         
28703         Roo.each(this.childForms || [], function (f) {
28704             f.clearInvalid();
28705         });
28706         
28707         
28708         return this;
28709     },
28710
28711     /**
28712      * Resets this form.
28713      * @return {BasicForm} this
28714      */
28715     reset : function(){
28716         this.items.each(function(f){
28717             f.reset();
28718         });
28719         
28720         Roo.each(this.childForms || [], function (f) {
28721             f.reset();
28722         });
28723        
28724         
28725         return this;
28726     },
28727
28728     /**
28729      * Add Roo.form components to this form.
28730      * @param {Field} field1
28731      * @param {Field} field2 (optional)
28732      * @param {Field} etc (optional)
28733      * @return {BasicForm} this
28734      */
28735     add : function(){
28736         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28737         return this;
28738     },
28739
28740
28741     /**
28742      * Removes a field from the items collection (does NOT remove its markup).
28743      * @param {Field} field
28744      * @return {BasicForm} this
28745      */
28746     remove : function(field){
28747         this.items.remove(field);
28748         return this;
28749     },
28750
28751     /**
28752      * Looks at the fields in this form, checks them for an id attribute,
28753      * and calls applyTo on the existing dom element with that id.
28754      * @return {BasicForm} this
28755      */
28756     render : function(){
28757         this.items.each(function(f){
28758             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28759                 f.applyTo(f.id);
28760             }
28761         });
28762         return this;
28763     },
28764
28765     /**
28766      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28767      * @param {Object} values
28768      * @return {BasicForm} this
28769      */
28770     applyToFields : function(o){
28771         this.items.each(function(f){
28772            Roo.apply(f, o);
28773         });
28774         return this;
28775     },
28776
28777     /**
28778      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28779      * @param {Object} values
28780      * @return {BasicForm} this
28781      */
28782     applyIfToFields : function(o){
28783         this.items.each(function(f){
28784            Roo.applyIf(f, o);
28785         });
28786         return this;
28787     }
28788 });
28789
28790 // back compat
28791 Roo.BasicForm = Roo.form.BasicForm;/*
28792  * Based on:
28793  * Ext JS Library 1.1.1
28794  * Copyright(c) 2006-2007, Ext JS, LLC.
28795  *
28796  * Originally Released Under LGPL - original licence link has changed is not relivant.
28797  *
28798  * Fork - LGPL
28799  * <script type="text/javascript">
28800  */
28801
28802 /**
28803  * @class Roo.form.Form
28804  * @extends Roo.form.BasicForm
28805  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28806  * @constructor
28807  * @param {Object} config Configuration options
28808  */
28809 Roo.form.Form = function(config){
28810     var xitems =  [];
28811     if (config.items) {
28812         xitems = config.items;
28813         delete config.items;
28814     }
28815    
28816     
28817     Roo.form.Form.superclass.constructor.call(this, null, config);
28818     this.url = this.url || this.action;
28819     if(!this.root){
28820         this.root = new Roo.form.Layout(Roo.applyIf({
28821             id: Roo.id()
28822         }, config));
28823     }
28824     this.active = this.root;
28825     /**
28826      * Array of all the buttons that have been added to this form via {@link addButton}
28827      * @type Array
28828      */
28829     this.buttons = [];
28830     this.allItems = [];
28831     this.addEvents({
28832         /**
28833          * @event clientvalidation
28834          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28835          * @param {Form} this
28836          * @param {Boolean} valid true if the form has passed client-side validation
28837          */
28838         clientvalidation: true,
28839         /**
28840          * @event rendered
28841          * Fires when the form is rendered
28842          * @param {Roo.form.Form} form
28843          */
28844         rendered : true
28845     });
28846     
28847     if (this.progressUrl) {
28848             // push a hidden field onto the list of fields..
28849             this.addxtype( {
28850                     xns: Roo.form, 
28851                     xtype : 'Hidden', 
28852                     name : 'UPLOAD_IDENTIFIER' 
28853             });
28854         }
28855         
28856     
28857     Roo.each(xitems, this.addxtype, this);
28858     
28859     
28860     
28861 };
28862
28863 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28864     /**
28865      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28866      */
28867     /**
28868      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28869      */
28870     /**
28871      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28872      */
28873     buttonAlign:'center',
28874
28875     /**
28876      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28877      */
28878     minButtonWidth:75,
28879
28880     /**
28881      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28882      * This property cascades to child containers if not set.
28883      */
28884     labelAlign:'left',
28885
28886     /**
28887      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28888      * fires a looping event with that state. This is required to bind buttons to the valid
28889      * state using the config value formBind:true on the button.
28890      */
28891     monitorValid : false,
28892
28893     /**
28894      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28895      */
28896     monitorPoll : 200,
28897     
28898     /**
28899      * @cfg {String} progressUrl - Url to return progress data 
28900      */
28901     
28902     progressUrl : false,
28903   
28904     /**
28905      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28906      * fields are added and the column is closed. If no fields are passed the column remains open
28907      * until end() is called.
28908      * @param {Object} config The config to pass to the column
28909      * @param {Field} field1 (optional)
28910      * @param {Field} field2 (optional)
28911      * @param {Field} etc (optional)
28912      * @return Column The column container object
28913      */
28914     column : function(c){
28915         var col = new Roo.form.Column(c);
28916         this.start(col);
28917         if(arguments.length > 1){ // duplicate code required because of Opera
28918             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28919             this.end();
28920         }
28921         return col;
28922     },
28923
28924     /**
28925      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28926      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28927      * until end() is called.
28928      * @param {Object} config The config to pass to the fieldset
28929      * @param {Field} field1 (optional)
28930      * @param {Field} field2 (optional)
28931      * @param {Field} etc (optional)
28932      * @return FieldSet The fieldset container object
28933      */
28934     fieldset : function(c){
28935         var fs = new Roo.form.FieldSet(c);
28936         this.start(fs);
28937         if(arguments.length > 1){ // duplicate code required because of Opera
28938             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28939             this.end();
28940         }
28941         return fs;
28942     },
28943
28944     /**
28945      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28946      * fields are added and the container is closed. If no fields are passed the container remains open
28947      * until end() is called.
28948      * @param {Object} config The config to pass to the Layout
28949      * @param {Field} field1 (optional)
28950      * @param {Field} field2 (optional)
28951      * @param {Field} etc (optional)
28952      * @return Layout The container object
28953      */
28954     container : function(c){
28955         var l = new Roo.form.Layout(c);
28956         this.start(l);
28957         if(arguments.length > 1){ // duplicate code required because of Opera
28958             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28959             this.end();
28960         }
28961         return l;
28962     },
28963
28964     /**
28965      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28966      * @param {Object} container A Roo.form.Layout or subclass of Layout
28967      * @return {Form} this
28968      */
28969     start : function(c){
28970         // cascade label info
28971         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28972         this.active.stack.push(c);
28973         c.ownerCt = this.active;
28974         this.active = c;
28975         return this;
28976     },
28977
28978     /**
28979      * Closes the current open container
28980      * @return {Form} this
28981      */
28982     end : function(){
28983         if(this.active == this.root){
28984             return this;
28985         }
28986         this.active = this.active.ownerCt;
28987         return this;
28988     },
28989
28990     /**
28991      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28992      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28993      * as the label of the field.
28994      * @param {Field} field1
28995      * @param {Field} field2 (optional)
28996      * @param {Field} etc. (optional)
28997      * @return {Form} this
28998      */
28999     add : function(){
29000         this.active.stack.push.apply(this.active.stack, arguments);
29001         this.allItems.push.apply(this.allItems,arguments);
29002         var r = [];
29003         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29004             if(a[i].isFormField){
29005                 r.push(a[i]);
29006             }
29007         }
29008         if(r.length > 0){
29009             Roo.form.Form.superclass.add.apply(this, r);
29010         }
29011         return this;
29012     },
29013     
29014
29015     
29016     
29017     
29018      /**
29019      * Find any element that has been added to a form, using it's ID or name
29020      * This can include framesets, columns etc. along with regular fields..
29021      * @param {String} id - id or name to find.
29022      
29023      * @return {Element} e - or false if nothing found.
29024      */
29025     findbyId : function(id)
29026     {
29027         var ret = false;
29028         if (!id) {
29029             return ret;
29030         }
29031         Roo.each(this.allItems, function(f){
29032             if (f.id == id || f.name == id ){
29033                 ret = f;
29034                 return false;
29035             }
29036         });
29037         return ret;
29038     },
29039
29040     
29041     
29042     /**
29043      * Render this form into the passed container. This should only be called once!
29044      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29045      * @return {Form} this
29046      */
29047     render : function(ct)
29048     {
29049         
29050         
29051         
29052         ct = Roo.get(ct);
29053         var o = this.autoCreate || {
29054             tag: 'form',
29055             method : this.method || 'POST',
29056             id : this.id || Roo.id()
29057         };
29058         this.initEl(ct.createChild(o));
29059
29060         this.root.render(this.el);
29061         
29062        
29063              
29064         this.items.each(function(f){
29065             f.render('x-form-el-'+f.id);
29066         });
29067
29068         if(this.buttons.length > 0){
29069             // tables are required to maintain order and for correct IE layout
29070             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29071                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29072                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29073             }}, null, true);
29074             var tr = tb.getElementsByTagName('tr')[0];
29075             for(var i = 0, len = this.buttons.length; i < len; i++) {
29076                 var b = this.buttons[i];
29077                 var td = document.createElement('td');
29078                 td.className = 'x-form-btn-td';
29079                 b.render(tr.appendChild(td));
29080             }
29081         }
29082         if(this.monitorValid){ // initialize after render
29083             this.startMonitoring();
29084         }
29085         this.fireEvent('rendered', this);
29086         return this;
29087     },
29088
29089     /**
29090      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29091      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29092      * object or a valid Roo.DomHelper element config
29093      * @param {Function} handler The function called when the button is clicked
29094      * @param {Object} scope (optional) The scope of the handler function
29095      * @return {Roo.Button}
29096      */
29097     addButton : function(config, handler, scope){
29098         var bc = {
29099             handler: handler,
29100             scope: scope,
29101             minWidth: this.minButtonWidth,
29102             hideParent:true
29103         };
29104         if(typeof config == "string"){
29105             bc.text = config;
29106         }else{
29107             Roo.apply(bc, config);
29108         }
29109         var btn = new Roo.Button(null, bc);
29110         this.buttons.push(btn);
29111         return btn;
29112     },
29113
29114      /**
29115      * Adds a series of form elements (using the xtype property as the factory method.
29116      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29117      * @param {Object} config 
29118      */
29119     
29120     addxtype : function()
29121     {
29122         var ar = Array.prototype.slice.call(arguments, 0);
29123         var ret = false;
29124         for(var i = 0; i < ar.length; i++) {
29125             if (!ar[i]) {
29126                 continue; // skip -- if this happends something invalid got sent, we 
29127                 // should ignore it, as basically that interface element will not show up
29128                 // and that should be pretty obvious!!
29129             }
29130             
29131             if (Roo.form[ar[i].xtype]) {
29132                 ar[i].form = this;
29133                 var fe = Roo.factory(ar[i], Roo.form);
29134                 if (!ret) {
29135                     ret = fe;
29136                 }
29137                 fe.form = this;
29138                 if (fe.store) {
29139                     fe.store.form = this;
29140                 }
29141                 if (fe.isLayout) {  
29142                          
29143                     this.start(fe);
29144                     this.allItems.push(fe);
29145                     if (fe.items && fe.addxtype) {
29146                         fe.addxtype.apply(fe, fe.items);
29147                         delete fe.items;
29148                     }
29149                      this.end();
29150                     continue;
29151                 }
29152                 
29153                 
29154                  
29155                 this.add(fe);
29156               //  console.log('adding ' + ar[i].xtype);
29157             }
29158             if (ar[i].xtype == 'Button') {  
29159                 //console.log('adding button');
29160                 //console.log(ar[i]);
29161                 this.addButton(ar[i]);
29162                 this.allItems.push(fe);
29163                 continue;
29164             }
29165             
29166             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29167                 alert('end is not supported on xtype any more, use items');
29168             //    this.end();
29169             //    //console.log('adding end');
29170             }
29171             
29172         }
29173         return ret;
29174     },
29175     
29176     /**
29177      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29178      * option "monitorValid"
29179      */
29180     startMonitoring : function(){
29181         if(!this.bound){
29182             this.bound = true;
29183             Roo.TaskMgr.start({
29184                 run : this.bindHandler,
29185                 interval : this.monitorPoll || 200,
29186                 scope: this
29187             });
29188         }
29189     },
29190
29191     /**
29192      * Stops monitoring of the valid state of this form
29193      */
29194     stopMonitoring : function(){
29195         this.bound = false;
29196     },
29197
29198     // private
29199     bindHandler : function(){
29200         if(!this.bound){
29201             return false; // stops binding
29202         }
29203         var valid = true;
29204         this.items.each(function(f){
29205             if(!f.isValid(true)){
29206                 valid = false;
29207                 return false;
29208             }
29209         });
29210         for(var i = 0, len = this.buttons.length; i < len; i++){
29211             var btn = this.buttons[i];
29212             if(btn.formBind === true && btn.disabled === valid){
29213                 btn.setDisabled(!valid);
29214             }
29215         }
29216         this.fireEvent('clientvalidation', this, valid);
29217     }
29218     
29219     
29220     
29221     
29222     
29223     
29224     
29225     
29226 });
29227
29228
29229 // back compat
29230 Roo.Form = Roo.form.Form;
29231 /*
29232  * Based on:
29233  * Ext JS Library 1.1.1
29234  * Copyright(c) 2006-2007, Ext JS, LLC.
29235  *
29236  * Originally Released Under LGPL - original licence link has changed is not relivant.
29237  *
29238  * Fork - LGPL
29239  * <script type="text/javascript">
29240  */
29241
29242 // as we use this in bootstrap.
29243 Roo.namespace('Roo.form');
29244  /**
29245  * @class Roo.form.Action
29246  * Internal Class used to handle form actions
29247  * @constructor
29248  * @param {Roo.form.BasicForm} el The form element or its id
29249  * @param {Object} config Configuration options
29250  */
29251
29252  
29253  
29254 // define the action interface
29255 Roo.form.Action = function(form, options){
29256     this.form = form;
29257     this.options = options || {};
29258 };
29259 /**
29260  * Client Validation Failed
29261  * @const 
29262  */
29263 Roo.form.Action.CLIENT_INVALID = 'client';
29264 /**
29265  * Server Validation Failed
29266  * @const 
29267  */
29268 Roo.form.Action.SERVER_INVALID = 'server';
29269  /**
29270  * Connect to Server Failed
29271  * @const 
29272  */
29273 Roo.form.Action.CONNECT_FAILURE = 'connect';
29274 /**
29275  * Reading Data from Server Failed
29276  * @const 
29277  */
29278 Roo.form.Action.LOAD_FAILURE = 'load';
29279
29280 Roo.form.Action.prototype = {
29281     type : 'default',
29282     failureType : undefined,
29283     response : undefined,
29284     result : undefined,
29285
29286     // interface method
29287     run : function(options){
29288
29289     },
29290
29291     // interface method
29292     success : function(response){
29293
29294     },
29295
29296     // interface method
29297     handleResponse : function(response){
29298
29299     },
29300
29301     // default connection failure
29302     failure : function(response){
29303         
29304         this.response = response;
29305         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29306         this.form.afterAction(this, false);
29307     },
29308
29309     processResponse : function(response){
29310         this.response = response;
29311         if(!response.responseText){
29312             return true;
29313         }
29314         this.result = this.handleResponse(response);
29315         return this.result;
29316     },
29317
29318     // utility functions used internally
29319     getUrl : function(appendParams){
29320         var url = this.options.url || this.form.url || this.form.el.dom.action;
29321         if(appendParams){
29322             var p = this.getParams();
29323             if(p){
29324                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29325             }
29326         }
29327         return url;
29328     },
29329
29330     getMethod : function(){
29331         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29332     },
29333
29334     getParams : function(){
29335         var bp = this.form.baseParams;
29336         var p = this.options.params;
29337         if(p){
29338             if(typeof p == "object"){
29339                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29340             }else if(typeof p == 'string' && bp){
29341                 p += '&' + Roo.urlEncode(bp);
29342             }
29343         }else if(bp){
29344             p = Roo.urlEncode(bp);
29345         }
29346         return p;
29347     },
29348
29349     createCallback : function(){
29350         return {
29351             success: this.success,
29352             failure: this.failure,
29353             scope: this,
29354             timeout: (this.form.timeout*1000),
29355             upload: this.form.fileUpload ? this.success : undefined
29356         };
29357     }
29358 };
29359
29360 Roo.form.Action.Submit = function(form, options){
29361     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29362 };
29363
29364 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29365     type : 'submit',
29366
29367     haveProgress : false,
29368     uploadComplete : false,
29369     
29370     // uploadProgress indicator.
29371     uploadProgress : function()
29372     {
29373         if (!this.form.progressUrl) {
29374             return;
29375         }
29376         
29377         if (!this.haveProgress) {
29378             Roo.MessageBox.progress("Uploading", "Uploading");
29379         }
29380         if (this.uploadComplete) {
29381            Roo.MessageBox.hide();
29382            return;
29383         }
29384         
29385         this.haveProgress = true;
29386    
29387         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29388         
29389         var c = new Roo.data.Connection();
29390         c.request({
29391             url : this.form.progressUrl,
29392             params: {
29393                 id : uid
29394             },
29395             method: 'GET',
29396             success : function(req){
29397                //console.log(data);
29398                 var rdata = false;
29399                 var edata;
29400                 try  {
29401                    rdata = Roo.decode(req.responseText)
29402                 } catch (e) {
29403                     Roo.log("Invalid data from server..");
29404                     Roo.log(edata);
29405                     return;
29406                 }
29407                 if (!rdata || !rdata.success) {
29408                     Roo.log(rdata);
29409                     Roo.MessageBox.alert(Roo.encode(rdata));
29410                     return;
29411                 }
29412                 var data = rdata.data;
29413                 
29414                 if (this.uploadComplete) {
29415                    Roo.MessageBox.hide();
29416                    return;
29417                 }
29418                    
29419                 if (data){
29420                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29421                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29422                     );
29423                 }
29424                 this.uploadProgress.defer(2000,this);
29425             },
29426        
29427             failure: function(data) {
29428                 Roo.log('progress url failed ');
29429                 Roo.log(data);
29430             },
29431             scope : this
29432         });
29433            
29434     },
29435     
29436     
29437     run : function()
29438     {
29439         // run get Values on the form, so it syncs any secondary forms.
29440         this.form.getValues();
29441         
29442         var o = this.options;
29443         var method = this.getMethod();
29444         var isPost = method == 'POST';
29445         if(o.clientValidation === false || this.form.isValid()){
29446             
29447             if (this.form.progressUrl) {
29448                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29449                     (new Date() * 1) + '' + Math.random());
29450                     
29451             } 
29452             
29453             
29454             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29455                 form:this.form.el.dom,
29456                 url:this.getUrl(!isPost),
29457                 method: method,
29458                 params:isPost ? this.getParams() : null,
29459                 isUpload: this.form.fileUpload
29460             }));
29461             
29462             this.uploadProgress();
29463
29464         }else if (o.clientValidation !== false){ // client validation failed
29465             this.failureType = Roo.form.Action.CLIENT_INVALID;
29466             this.form.afterAction(this, false);
29467         }
29468     },
29469
29470     success : function(response)
29471     {
29472         this.uploadComplete= true;
29473         if (this.haveProgress) {
29474             Roo.MessageBox.hide();
29475         }
29476         
29477         
29478         var result = this.processResponse(response);
29479         if(result === true || result.success){
29480             this.form.afterAction(this, true);
29481             return;
29482         }
29483         if(result.errors){
29484             this.form.markInvalid(result.errors);
29485             this.failureType = Roo.form.Action.SERVER_INVALID;
29486         }
29487         this.form.afterAction(this, false);
29488     },
29489     failure : function(response)
29490     {
29491         this.uploadComplete= true;
29492         if (this.haveProgress) {
29493             Roo.MessageBox.hide();
29494         }
29495         
29496         this.response = response;
29497         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29498         this.form.afterAction(this, false);
29499     },
29500     
29501     handleResponse : function(response){
29502         if(this.form.errorReader){
29503             var rs = this.form.errorReader.read(response);
29504             var errors = [];
29505             if(rs.records){
29506                 for(var i = 0, len = rs.records.length; i < len; i++) {
29507                     var r = rs.records[i];
29508                     errors[i] = r.data;
29509                 }
29510             }
29511             if(errors.length < 1){
29512                 errors = null;
29513             }
29514             return {
29515                 success : rs.success,
29516                 errors : errors
29517             };
29518         }
29519         var ret = false;
29520         try {
29521             ret = Roo.decode(response.responseText);
29522         } catch (e) {
29523             ret = {
29524                 success: false,
29525                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29526                 errors : []
29527             };
29528         }
29529         return ret;
29530         
29531     }
29532 });
29533
29534
29535 Roo.form.Action.Load = function(form, options){
29536     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29537     this.reader = this.form.reader;
29538 };
29539
29540 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29541     type : 'load',
29542
29543     run : function(){
29544         
29545         Roo.Ajax.request(Roo.apply(
29546                 this.createCallback(), {
29547                     method:this.getMethod(),
29548                     url:this.getUrl(false),
29549                     params:this.getParams()
29550         }));
29551     },
29552
29553     success : function(response){
29554         
29555         var result = this.processResponse(response);
29556         if(result === true || !result.success || !result.data){
29557             this.failureType = Roo.form.Action.LOAD_FAILURE;
29558             this.form.afterAction(this, false);
29559             return;
29560         }
29561         this.form.clearInvalid();
29562         this.form.setValues(result.data);
29563         this.form.afterAction(this, true);
29564     },
29565
29566     handleResponse : function(response){
29567         if(this.form.reader){
29568             var rs = this.form.reader.read(response);
29569             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29570             return {
29571                 success : rs.success,
29572                 data : data
29573             };
29574         }
29575         return Roo.decode(response.responseText);
29576     }
29577 });
29578
29579 Roo.form.Action.ACTION_TYPES = {
29580     'load' : Roo.form.Action.Load,
29581     'submit' : Roo.form.Action.Submit
29582 };/*
29583  * Based on:
29584  * Ext JS Library 1.1.1
29585  * Copyright(c) 2006-2007, Ext JS, LLC.
29586  *
29587  * Originally Released Under LGPL - original licence link has changed is not relivant.
29588  *
29589  * Fork - LGPL
29590  * <script type="text/javascript">
29591  */
29592  
29593 /**
29594  * @class Roo.form.Layout
29595  * @extends Roo.Component
29596  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29597  * @constructor
29598  * @param {Object} config Configuration options
29599  */
29600 Roo.form.Layout = function(config){
29601     var xitems = [];
29602     if (config.items) {
29603         xitems = config.items;
29604         delete config.items;
29605     }
29606     Roo.form.Layout.superclass.constructor.call(this, config);
29607     this.stack = [];
29608     Roo.each(xitems, this.addxtype, this);
29609      
29610 };
29611
29612 Roo.extend(Roo.form.Layout, Roo.Component, {
29613     /**
29614      * @cfg {String/Object} autoCreate
29615      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29616      */
29617     /**
29618      * @cfg {String/Object/Function} style
29619      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29620      * a function which returns such a specification.
29621      */
29622     /**
29623      * @cfg {String} labelAlign
29624      * Valid values are "left," "top" and "right" (defaults to "left")
29625      */
29626     /**
29627      * @cfg {Number} labelWidth
29628      * Fixed width in pixels of all field labels (defaults to undefined)
29629      */
29630     /**
29631      * @cfg {Boolean} clear
29632      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29633      */
29634     clear : true,
29635     /**
29636      * @cfg {String} labelSeparator
29637      * The separator to use after field labels (defaults to ':')
29638      */
29639     labelSeparator : ':',
29640     /**
29641      * @cfg {Boolean} hideLabels
29642      * True to suppress the display of field labels in this layout (defaults to false)
29643      */
29644     hideLabels : false,
29645
29646     // private
29647     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29648     
29649     isLayout : true,
29650     
29651     // private
29652     onRender : function(ct, position){
29653         if(this.el){ // from markup
29654             this.el = Roo.get(this.el);
29655         }else {  // generate
29656             var cfg = this.getAutoCreate();
29657             this.el = ct.createChild(cfg, position);
29658         }
29659         if(this.style){
29660             this.el.applyStyles(this.style);
29661         }
29662         if(this.labelAlign){
29663             this.el.addClass('x-form-label-'+this.labelAlign);
29664         }
29665         if(this.hideLabels){
29666             this.labelStyle = "display:none";
29667             this.elementStyle = "padding-left:0;";
29668         }else{
29669             if(typeof this.labelWidth == 'number'){
29670                 this.labelStyle = "width:"+this.labelWidth+"px;";
29671                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29672             }
29673             if(this.labelAlign == 'top'){
29674                 this.labelStyle = "width:auto;";
29675                 this.elementStyle = "padding-left:0;";
29676             }
29677         }
29678         var stack = this.stack;
29679         var slen = stack.length;
29680         if(slen > 0){
29681             if(!this.fieldTpl){
29682                 var t = new Roo.Template(
29683                     '<div class="x-form-item {5}">',
29684                         '<label for="{0}" style="{2}">{1}{4}</label>',
29685                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29686                         '</div>',
29687                     '</div><div class="x-form-clear-left"></div>'
29688                 );
29689                 t.disableFormats = true;
29690                 t.compile();
29691                 Roo.form.Layout.prototype.fieldTpl = t;
29692             }
29693             for(var i = 0; i < slen; i++) {
29694                 if(stack[i].isFormField){
29695                     this.renderField(stack[i]);
29696                 }else{
29697                     this.renderComponent(stack[i]);
29698                 }
29699             }
29700         }
29701         if(this.clear){
29702             this.el.createChild({cls:'x-form-clear'});
29703         }
29704     },
29705
29706     // private
29707     renderField : function(f){
29708         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29709                f.id, //0
29710                f.fieldLabel, //1
29711                f.labelStyle||this.labelStyle||'', //2
29712                this.elementStyle||'', //3
29713                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29714                f.itemCls||this.itemCls||''  //5
29715        ], true).getPrevSibling());
29716     },
29717
29718     // private
29719     renderComponent : function(c){
29720         c.render(c.isLayout ? this.el : this.el.createChild());    
29721     },
29722     /**
29723      * Adds a object form elements (using the xtype property as the factory method.)
29724      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29725      * @param {Object} config 
29726      */
29727     addxtype : function(o)
29728     {
29729         // create the lement.
29730         o.form = this.form;
29731         var fe = Roo.factory(o, Roo.form);
29732         this.form.allItems.push(fe);
29733         this.stack.push(fe);
29734         
29735         if (fe.isFormField) {
29736             this.form.items.add(fe);
29737         }
29738          
29739         return fe;
29740     }
29741 });
29742
29743 /**
29744  * @class Roo.form.Column
29745  * @extends Roo.form.Layout
29746  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29747  * @constructor
29748  * @param {Object} config Configuration options
29749  */
29750 Roo.form.Column = function(config){
29751     Roo.form.Column.superclass.constructor.call(this, config);
29752 };
29753
29754 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29755     /**
29756      * @cfg {Number/String} width
29757      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29758      */
29759     /**
29760      * @cfg {String/Object} autoCreate
29761      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29762      */
29763
29764     // private
29765     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29766
29767     // private
29768     onRender : function(ct, position){
29769         Roo.form.Column.superclass.onRender.call(this, ct, position);
29770         if(this.width){
29771             this.el.setWidth(this.width);
29772         }
29773     }
29774 });
29775
29776
29777 /**
29778  * @class Roo.form.Row
29779  * @extends Roo.form.Layout
29780  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29781  * @constructor
29782  * @param {Object} config Configuration options
29783  */
29784
29785  
29786 Roo.form.Row = function(config){
29787     Roo.form.Row.superclass.constructor.call(this, config);
29788 };
29789  
29790 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29791       /**
29792      * @cfg {Number/String} width
29793      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29794      */
29795     /**
29796      * @cfg {Number/String} height
29797      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29798      */
29799     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29800     
29801     padWidth : 20,
29802     // private
29803     onRender : function(ct, position){
29804         //console.log('row render');
29805         if(!this.rowTpl){
29806             var t = new Roo.Template(
29807                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29808                     '<label for="{0}" style="{2}">{1}{4}</label>',
29809                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29810                     '</div>',
29811                 '</div>'
29812             );
29813             t.disableFormats = true;
29814             t.compile();
29815             Roo.form.Layout.prototype.rowTpl = t;
29816         }
29817         this.fieldTpl = this.rowTpl;
29818         
29819         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29820         var labelWidth = 100;
29821         
29822         if ((this.labelAlign != 'top')) {
29823             if (typeof this.labelWidth == 'number') {
29824                 labelWidth = this.labelWidth
29825             }
29826             this.padWidth =  20 + labelWidth;
29827             
29828         }
29829         
29830         Roo.form.Column.superclass.onRender.call(this, ct, position);
29831         if(this.width){
29832             this.el.setWidth(this.width);
29833         }
29834         if(this.height){
29835             this.el.setHeight(this.height);
29836         }
29837     },
29838     
29839     // private
29840     renderField : function(f){
29841         f.fieldEl = this.fieldTpl.append(this.el, [
29842                f.id, f.fieldLabel,
29843                f.labelStyle||this.labelStyle||'',
29844                this.elementStyle||'',
29845                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29846                f.itemCls||this.itemCls||'',
29847                f.width ? f.width + this.padWidth : 160 + this.padWidth
29848        ],true);
29849     }
29850 });
29851  
29852
29853 /**
29854  * @class Roo.form.FieldSet
29855  * @extends Roo.form.Layout
29856  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29857  * @constructor
29858  * @param {Object} config Configuration options
29859  */
29860 Roo.form.FieldSet = function(config){
29861     Roo.form.FieldSet.superclass.constructor.call(this, config);
29862 };
29863
29864 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29865     /**
29866      * @cfg {String} legend
29867      * The text to display as the legend for the FieldSet (defaults to '')
29868      */
29869     /**
29870      * @cfg {String/Object} autoCreate
29871      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29872      */
29873
29874     // private
29875     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29876
29877     // private
29878     onRender : function(ct, position){
29879         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29880         if(this.legend){
29881             this.setLegend(this.legend);
29882         }
29883     },
29884
29885     // private
29886     setLegend : function(text){
29887         if(this.rendered){
29888             this.el.child('legend').update(text);
29889         }
29890     }
29891 });/*
29892  * Based on:
29893  * Ext JS Library 1.1.1
29894  * Copyright(c) 2006-2007, Ext JS, LLC.
29895  *
29896  * Originally Released Under LGPL - original licence link has changed is not relivant.
29897  *
29898  * Fork - LGPL
29899  * <script type="text/javascript">
29900  */
29901 /**
29902  * @class Roo.form.VTypes
29903  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29904  * @singleton
29905  */
29906 Roo.form.VTypes = function(){
29907     // closure these in so they are only created once.
29908     var alpha = /^[a-zA-Z_]+$/;
29909     var alphanum = /^[a-zA-Z0-9_]+$/;
29910     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29911     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29912
29913     // All these messages and functions are configurable
29914     return {
29915         /**
29916          * The function used to validate email addresses
29917          * @param {String} value The email address
29918          */
29919         'email' : function(v){
29920             return email.test(v);
29921         },
29922         /**
29923          * The error text to display when the email validation function returns false
29924          * @type String
29925          */
29926         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29927         /**
29928          * The keystroke filter mask to be applied on email input
29929          * @type RegExp
29930          */
29931         'emailMask' : /[a-z0-9_\.\-@]/i,
29932
29933         /**
29934          * The function used to validate URLs
29935          * @param {String} value The URL
29936          */
29937         'url' : function(v){
29938             return url.test(v);
29939         },
29940         /**
29941          * The error text to display when the url validation function returns false
29942          * @type String
29943          */
29944         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29945         
29946         /**
29947          * The function used to validate alpha values
29948          * @param {String} value The value
29949          */
29950         'alpha' : function(v){
29951             return alpha.test(v);
29952         },
29953         /**
29954          * The error text to display when the alpha validation function returns false
29955          * @type String
29956          */
29957         'alphaText' : 'This field should only contain letters and _',
29958         /**
29959          * The keystroke filter mask to be applied on alpha input
29960          * @type RegExp
29961          */
29962         'alphaMask' : /[a-z_]/i,
29963
29964         /**
29965          * The function used to validate alphanumeric values
29966          * @param {String} value The value
29967          */
29968         'alphanum' : function(v){
29969             return alphanum.test(v);
29970         },
29971         /**
29972          * The error text to display when the alphanumeric validation function returns false
29973          * @type String
29974          */
29975         'alphanumText' : 'This field should only contain letters, numbers and _',
29976         /**
29977          * The keystroke filter mask to be applied on alphanumeric input
29978          * @type RegExp
29979          */
29980         'alphanumMask' : /[a-z0-9_]/i
29981     };
29982 }();//<script type="text/javascript">
29983
29984 /**
29985  * @class Roo.form.FCKeditor
29986  * @extends Roo.form.TextArea
29987  * Wrapper around the FCKEditor http://www.fckeditor.net
29988  * @constructor
29989  * Creates a new FCKeditor
29990  * @param {Object} config Configuration options
29991  */
29992 Roo.form.FCKeditor = function(config){
29993     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29994     this.addEvents({
29995          /**
29996          * @event editorinit
29997          * Fired when the editor is initialized - you can add extra handlers here..
29998          * @param {FCKeditor} this
29999          * @param {Object} the FCK object.
30000          */
30001         editorinit : true
30002     });
30003     
30004     
30005 };
30006 Roo.form.FCKeditor.editors = { };
30007 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30008 {
30009     //defaultAutoCreate : {
30010     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30011     //},
30012     // private
30013     /**
30014      * @cfg {Object} fck options - see fck manual for details.
30015      */
30016     fckconfig : false,
30017     
30018     /**
30019      * @cfg {Object} fck toolbar set (Basic or Default)
30020      */
30021     toolbarSet : 'Basic',
30022     /**
30023      * @cfg {Object} fck BasePath
30024      */ 
30025     basePath : '/fckeditor/',
30026     
30027     
30028     frame : false,
30029     
30030     value : '',
30031     
30032    
30033     onRender : function(ct, position)
30034     {
30035         if(!this.el){
30036             this.defaultAutoCreate = {
30037                 tag: "textarea",
30038                 style:"width:300px;height:60px;",
30039                 autocomplete: "off"
30040             };
30041         }
30042         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30043         /*
30044         if(this.grow){
30045             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30046             if(this.preventScrollbars){
30047                 this.el.setStyle("overflow", "hidden");
30048             }
30049             this.el.setHeight(this.growMin);
30050         }
30051         */
30052         //console.log('onrender' + this.getId() );
30053         Roo.form.FCKeditor.editors[this.getId()] = this;
30054          
30055
30056         this.replaceTextarea() ;
30057         
30058     },
30059     
30060     getEditor : function() {
30061         return this.fckEditor;
30062     },
30063     /**
30064      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30065      * @param {Mixed} value The value to set
30066      */
30067     
30068     
30069     setValue : function(value)
30070     {
30071         //console.log('setValue: ' + value);
30072         
30073         if(typeof(value) == 'undefined') { // not sure why this is happending...
30074             return;
30075         }
30076         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30077         
30078         //if(!this.el || !this.getEditor()) {
30079         //    this.value = value;
30080             //this.setValue.defer(100,this,[value]);    
30081         //    return;
30082         //} 
30083         
30084         if(!this.getEditor()) {
30085             return;
30086         }
30087         
30088         this.getEditor().SetData(value);
30089         
30090         //
30091
30092     },
30093
30094     /**
30095      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30096      * @return {Mixed} value The field value
30097      */
30098     getValue : function()
30099     {
30100         
30101         if (this.frame && this.frame.dom.style.display == 'none') {
30102             return Roo.form.FCKeditor.superclass.getValue.call(this);
30103         }
30104         
30105         if(!this.el || !this.getEditor()) {
30106            
30107            // this.getValue.defer(100,this); 
30108             return this.value;
30109         }
30110        
30111         
30112         var value=this.getEditor().GetData();
30113         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30114         return Roo.form.FCKeditor.superclass.getValue.call(this);
30115         
30116
30117     },
30118
30119     /**
30120      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30121      * @return {Mixed} value The field value
30122      */
30123     getRawValue : function()
30124     {
30125         if (this.frame && this.frame.dom.style.display == 'none') {
30126             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30127         }
30128         
30129         if(!this.el || !this.getEditor()) {
30130             //this.getRawValue.defer(100,this); 
30131             return this.value;
30132             return;
30133         }
30134         
30135         
30136         
30137         var value=this.getEditor().GetData();
30138         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30139         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30140          
30141     },
30142     
30143     setSize : function(w,h) {
30144         
30145         
30146         
30147         //if (this.frame && this.frame.dom.style.display == 'none') {
30148         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30149         //    return;
30150         //}
30151         //if(!this.el || !this.getEditor()) {
30152         //    this.setSize.defer(100,this, [w,h]); 
30153         //    return;
30154         //}
30155         
30156         
30157         
30158         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30159         
30160         this.frame.dom.setAttribute('width', w);
30161         this.frame.dom.setAttribute('height', h);
30162         this.frame.setSize(w,h);
30163         
30164     },
30165     
30166     toggleSourceEdit : function(value) {
30167         
30168       
30169          
30170         this.el.dom.style.display = value ? '' : 'none';
30171         this.frame.dom.style.display = value ?  'none' : '';
30172         
30173     },
30174     
30175     
30176     focus: function(tag)
30177     {
30178         if (this.frame.dom.style.display == 'none') {
30179             return Roo.form.FCKeditor.superclass.focus.call(this);
30180         }
30181         if(!this.el || !this.getEditor()) {
30182             this.focus.defer(100,this, [tag]); 
30183             return;
30184         }
30185         
30186         
30187         
30188         
30189         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30190         this.getEditor().Focus();
30191         if (tgs.length) {
30192             if (!this.getEditor().Selection.GetSelection()) {
30193                 this.focus.defer(100,this, [tag]); 
30194                 return;
30195             }
30196             
30197             
30198             var r = this.getEditor().EditorDocument.createRange();
30199             r.setStart(tgs[0],0);
30200             r.setEnd(tgs[0],0);
30201             this.getEditor().Selection.GetSelection().removeAllRanges();
30202             this.getEditor().Selection.GetSelection().addRange(r);
30203             this.getEditor().Focus();
30204         }
30205         
30206     },
30207     
30208     
30209     
30210     replaceTextarea : function()
30211     {
30212         if ( document.getElementById( this.getId() + '___Frame' ) )
30213             return ;
30214         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30215         //{
30216             // We must check the elements firstly using the Id and then the name.
30217         var oTextarea = document.getElementById( this.getId() );
30218         
30219         var colElementsByName = document.getElementsByName( this.getId() ) ;
30220          
30221         oTextarea.style.display = 'none' ;
30222
30223         if ( oTextarea.tabIndex ) {            
30224             this.TabIndex = oTextarea.tabIndex ;
30225         }
30226         
30227         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30228         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30229         this.frame = Roo.get(this.getId() + '___Frame')
30230     },
30231     
30232     _getConfigHtml : function()
30233     {
30234         var sConfig = '' ;
30235
30236         for ( var o in this.fckconfig ) {
30237             sConfig += sConfig.length > 0  ? '&amp;' : '';
30238             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30239         }
30240
30241         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30242     },
30243     
30244     
30245     _getIFrameHtml : function()
30246     {
30247         var sFile = 'fckeditor.html' ;
30248         /* no idea what this is about..
30249         try
30250         {
30251             if ( (/fcksource=true/i).test( window.top.location.search ) )
30252                 sFile = 'fckeditor.original.html' ;
30253         }
30254         catch (e) { 
30255         */
30256
30257         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30258         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30259         
30260         
30261         var html = '<iframe id="' + this.getId() +
30262             '___Frame" src="' + sLink +
30263             '" width="' + this.width +
30264             '" height="' + this.height + '"' +
30265             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30266             ' frameborder="0" scrolling="no"></iframe>' ;
30267
30268         return html ;
30269     },
30270     
30271     _insertHtmlBefore : function( html, element )
30272     {
30273         if ( element.insertAdjacentHTML )       {
30274             // IE
30275             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30276         } else { // Gecko
30277             var oRange = document.createRange() ;
30278             oRange.setStartBefore( element ) ;
30279             var oFragment = oRange.createContextualFragment( html );
30280             element.parentNode.insertBefore( oFragment, element ) ;
30281         }
30282     }
30283     
30284     
30285   
30286     
30287     
30288     
30289     
30290
30291 });
30292
30293 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30294
30295 function FCKeditor_OnComplete(editorInstance){
30296     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30297     f.fckEditor = editorInstance;
30298     //console.log("loaded");
30299     f.fireEvent('editorinit', f, editorInstance);
30300
30301   
30302
30303  
30304
30305
30306
30307
30308
30309
30310
30311
30312
30313
30314
30315
30316
30317
30318
30319 //<script type="text/javascript">
30320 /**
30321  * @class Roo.form.GridField
30322  * @extends Roo.form.Field
30323  * Embed a grid (or editable grid into a form)
30324  * STATUS ALPHA
30325  * 
30326  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30327  * it needs 
30328  * xgrid.store = Roo.data.Store
30329  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30330  * xgrid.store.reader = Roo.data.JsonReader 
30331  * 
30332  * 
30333  * @constructor
30334  * Creates a new GridField
30335  * @param {Object} config Configuration options
30336  */
30337 Roo.form.GridField = function(config){
30338     Roo.form.GridField.superclass.constructor.call(this, config);
30339      
30340 };
30341
30342 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30343     /**
30344      * @cfg {Number} width  - used to restrict width of grid..
30345      */
30346     width : 100,
30347     /**
30348      * @cfg {Number} height - used to restrict height of grid..
30349      */
30350     height : 50,
30351      /**
30352      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30353          * 
30354          *}
30355      */
30356     xgrid : false, 
30357     /**
30358      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30359      * {tag: "input", type: "checkbox", autocomplete: "off"})
30360      */
30361    // defaultAutoCreate : { tag: 'div' },
30362     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30363     /**
30364      * @cfg {String} addTitle Text to include for adding a title.
30365      */
30366     addTitle : false,
30367     //
30368     onResize : function(){
30369         Roo.form.Field.superclass.onResize.apply(this, arguments);
30370     },
30371
30372     initEvents : function(){
30373         // Roo.form.Checkbox.superclass.initEvents.call(this);
30374         // has no events...
30375        
30376     },
30377
30378
30379     getResizeEl : function(){
30380         return this.wrap;
30381     },
30382
30383     getPositionEl : function(){
30384         return this.wrap;
30385     },
30386
30387     // private
30388     onRender : function(ct, position){
30389         
30390         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30391         var style = this.style;
30392         delete this.style;
30393         
30394         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30395         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30396         this.viewEl = this.wrap.createChild({ tag: 'div' });
30397         if (style) {
30398             this.viewEl.applyStyles(style);
30399         }
30400         if (this.width) {
30401             this.viewEl.setWidth(this.width);
30402         }
30403         if (this.height) {
30404             this.viewEl.setHeight(this.height);
30405         }
30406         //if(this.inputValue !== undefined){
30407         //this.setValue(this.value);
30408         
30409         
30410         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30411         
30412         
30413         this.grid.render();
30414         this.grid.getDataSource().on('remove', this.refreshValue, this);
30415         this.grid.getDataSource().on('update', this.refreshValue, this);
30416         this.grid.on('afteredit', this.refreshValue, this);
30417  
30418     },
30419      
30420     
30421     /**
30422      * Sets the value of the item. 
30423      * @param {String} either an object  or a string..
30424      */
30425     setValue : function(v){
30426         //this.value = v;
30427         v = v || []; // empty set..
30428         // this does not seem smart - it really only affects memoryproxy grids..
30429         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30430             var ds = this.grid.getDataSource();
30431             // assumes a json reader..
30432             var data = {}
30433             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30434             ds.loadData( data);
30435         }
30436         // clear selection so it does not get stale.
30437         if (this.grid.sm) { 
30438             this.grid.sm.clearSelections();
30439         }
30440         
30441         Roo.form.GridField.superclass.setValue.call(this, v);
30442         this.refreshValue();
30443         // should load data in the grid really....
30444     },
30445     
30446     // private
30447     refreshValue: function() {
30448          var val = [];
30449         this.grid.getDataSource().each(function(r) {
30450             val.push(r.data);
30451         });
30452         this.el.dom.value = Roo.encode(val);
30453     }
30454     
30455      
30456     
30457     
30458 });/*
30459  * Based on:
30460  * Ext JS Library 1.1.1
30461  * Copyright(c) 2006-2007, Ext JS, LLC.
30462  *
30463  * Originally Released Under LGPL - original licence link has changed is not relivant.
30464  *
30465  * Fork - LGPL
30466  * <script type="text/javascript">
30467  */
30468 /**
30469  * @class Roo.form.DisplayField
30470  * @extends Roo.form.Field
30471  * A generic Field to display non-editable data.
30472  * @constructor
30473  * Creates a new Display Field item.
30474  * @param {Object} config Configuration options
30475  */
30476 Roo.form.DisplayField = function(config){
30477     Roo.form.DisplayField.superclass.constructor.call(this, config);
30478     
30479 };
30480
30481 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30482     inputType:      'hidden',
30483     allowBlank:     true,
30484     readOnly:         true,
30485     
30486  
30487     /**
30488      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30489      */
30490     focusClass : undefined,
30491     /**
30492      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30493      */
30494     fieldClass: 'x-form-field',
30495     
30496      /**
30497      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30498      */
30499     valueRenderer: undefined,
30500     
30501     width: 100,
30502     /**
30503      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30504      * {tag: "input", type: "checkbox", autocomplete: "off"})
30505      */
30506      
30507  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30508
30509     onResize : function(){
30510         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30511         
30512     },
30513
30514     initEvents : function(){
30515         // Roo.form.Checkbox.superclass.initEvents.call(this);
30516         // has no events...
30517        
30518     },
30519
30520
30521     getResizeEl : function(){
30522         return this.wrap;
30523     },
30524
30525     getPositionEl : function(){
30526         return this.wrap;
30527     },
30528
30529     // private
30530     onRender : function(ct, position){
30531         
30532         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30533         //if(this.inputValue !== undefined){
30534         this.wrap = this.el.wrap();
30535         
30536         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30537         
30538         if (this.bodyStyle) {
30539             this.viewEl.applyStyles(this.bodyStyle);
30540         }
30541         //this.viewEl.setStyle('padding', '2px');
30542         
30543         this.setValue(this.value);
30544         
30545     },
30546 /*
30547     // private
30548     initValue : Roo.emptyFn,
30549
30550   */
30551
30552         // private
30553     onClick : function(){
30554         
30555     },
30556
30557     /**
30558      * Sets the checked state of the checkbox.
30559      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30560      */
30561     setValue : function(v){
30562         this.value = v;
30563         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30564         // this might be called before we have a dom element..
30565         if (!this.viewEl) {
30566             return;
30567         }
30568         this.viewEl.dom.innerHTML = html;
30569         Roo.form.DisplayField.superclass.setValue.call(this, v);
30570
30571     }
30572 });/*
30573  * 
30574  * Licence- LGPL
30575  * 
30576  */
30577
30578 /**
30579  * @class Roo.form.DayPicker
30580  * @extends Roo.form.Field
30581  * A Day picker show [M] [T] [W] ....
30582  * @constructor
30583  * Creates a new Day Picker
30584  * @param {Object} config Configuration options
30585  */
30586 Roo.form.DayPicker= function(config){
30587     Roo.form.DayPicker.superclass.constructor.call(this, config);
30588      
30589 };
30590
30591 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30592     /**
30593      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30594      */
30595     focusClass : undefined,
30596     /**
30597      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30598      */
30599     fieldClass: "x-form-field",
30600    
30601     /**
30602      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30603      * {tag: "input", type: "checkbox", autocomplete: "off"})
30604      */
30605     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30606     
30607    
30608     actionMode : 'viewEl', 
30609     //
30610     // private
30611  
30612     inputType : 'hidden',
30613     
30614      
30615     inputElement: false, // real input element?
30616     basedOn: false, // ????
30617     
30618     isFormField: true, // not sure where this is needed!!!!
30619
30620     onResize : function(){
30621         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30622         if(!this.boxLabel){
30623             this.el.alignTo(this.wrap, 'c-c');
30624         }
30625     },
30626
30627     initEvents : function(){
30628         Roo.form.Checkbox.superclass.initEvents.call(this);
30629         this.el.on("click", this.onClick,  this);
30630         this.el.on("change", this.onClick,  this);
30631     },
30632
30633
30634     getResizeEl : function(){
30635         return this.wrap;
30636     },
30637
30638     getPositionEl : function(){
30639         return this.wrap;
30640     },
30641
30642     
30643     // private
30644     onRender : function(ct, position){
30645         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30646        
30647         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30648         
30649         var r1 = '<table><tr>';
30650         var r2 = '<tr class="x-form-daypick-icons">';
30651         for (var i=0; i < 7; i++) {
30652             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30653             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30654         }
30655         
30656         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30657         viewEl.select('img').on('click', this.onClick, this);
30658         this.viewEl = viewEl;   
30659         
30660         
30661         // this will not work on Chrome!!!
30662         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30663         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30664         
30665         
30666           
30667
30668     },
30669
30670     // private
30671     initValue : Roo.emptyFn,
30672
30673     /**
30674      * Returns the checked state of the checkbox.
30675      * @return {Boolean} True if checked, else false
30676      */
30677     getValue : function(){
30678         return this.el.dom.value;
30679         
30680     },
30681
30682         // private
30683     onClick : function(e){ 
30684         //this.setChecked(!this.checked);
30685         Roo.get(e.target).toggleClass('x-menu-item-checked');
30686         this.refreshValue();
30687         //if(this.el.dom.checked != this.checked){
30688         //    this.setValue(this.el.dom.checked);
30689        // }
30690     },
30691     
30692     // private
30693     refreshValue : function()
30694     {
30695         var val = '';
30696         this.viewEl.select('img',true).each(function(e,i,n)  {
30697             val += e.is(".x-menu-item-checked") ? String(n) : '';
30698         });
30699         this.setValue(val, true);
30700     },
30701
30702     /**
30703      * Sets the checked state of the checkbox.
30704      * On is always based on a string comparison between inputValue and the param.
30705      * @param {Boolean/String} value - the value to set 
30706      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30707      */
30708     setValue : function(v,suppressEvent){
30709         if (!this.el.dom) {
30710             return;
30711         }
30712         var old = this.el.dom.value ;
30713         this.el.dom.value = v;
30714         if (suppressEvent) {
30715             return ;
30716         }
30717          
30718         // update display..
30719         this.viewEl.select('img',true).each(function(e,i,n)  {
30720             
30721             var on = e.is(".x-menu-item-checked");
30722             var newv = v.indexOf(String(n)) > -1;
30723             if (on != newv) {
30724                 e.toggleClass('x-menu-item-checked');
30725             }
30726             
30727         });
30728         
30729         
30730         this.fireEvent('change', this, v, old);
30731         
30732         
30733     },
30734    
30735     // handle setting of hidden value by some other method!!?!?
30736     setFromHidden: function()
30737     {
30738         if(!this.el){
30739             return;
30740         }
30741         //console.log("SET FROM HIDDEN");
30742         //alert('setFrom hidden');
30743         this.setValue(this.el.dom.value);
30744     },
30745     
30746     onDestroy : function()
30747     {
30748         if(this.viewEl){
30749             Roo.get(this.viewEl).remove();
30750         }
30751          
30752         Roo.form.DayPicker.superclass.onDestroy.call(this);
30753     }
30754
30755 });/*
30756  * RooJS Library 1.1.1
30757  * Copyright(c) 2008-2011  Alan Knowles
30758  *
30759  * License - LGPL
30760  */
30761  
30762
30763 /**
30764  * @class Roo.form.ComboCheck
30765  * @extends Roo.form.ComboBox
30766  * A combobox for multiple select items.
30767  *
30768  * FIXME - could do with a reset button..
30769  * 
30770  * @constructor
30771  * Create a new ComboCheck
30772  * @param {Object} config Configuration options
30773  */
30774 Roo.form.ComboCheck = function(config){
30775     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30776     // should verify some data...
30777     // like
30778     // hiddenName = required..
30779     // displayField = required
30780     // valudField == required
30781     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30782     var _t = this;
30783     Roo.each(req, function(e) {
30784         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30785             throw "Roo.form.ComboCheck : missing value for: " + e;
30786         }
30787     });
30788     
30789     
30790 };
30791
30792 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30793      
30794      
30795     editable : false,
30796      
30797     selectedClass: 'x-menu-item-checked', 
30798     
30799     // private
30800     onRender : function(ct, position){
30801         var _t = this;
30802         
30803         
30804         
30805         if(!this.tpl){
30806             var cls = 'x-combo-list';
30807
30808             
30809             this.tpl =  new Roo.Template({
30810                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30811                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30812                    '<span>{' + this.displayField + '}</span>' +
30813                     '</div>' 
30814                 
30815             });
30816         }
30817  
30818         
30819         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30820         this.view.singleSelect = false;
30821         this.view.multiSelect = true;
30822         this.view.toggleSelect = true;
30823         this.pageTb.add(new Roo.Toolbar.Fill(), {
30824             
30825             text: 'Done',
30826             handler: function()
30827             {
30828                 _t.collapse();
30829             }
30830         });
30831     },
30832     
30833     onViewOver : function(e, t){
30834         // do nothing...
30835         return;
30836         
30837     },
30838     
30839     onViewClick : function(doFocus,index){
30840         return;
30841         
30842     },
30843     select: function () {
30844         //Roo.log("SELECT CALLED");
30845     },
30846      
30847     selectByValue : function(xv, scrollIntoView){
30848         var ar = this.getValueArray();
30849         var sels = [];
30850         
30851         Roo.each(ar, function(v) {
30852             if(v === undefined || v === null){
30853                 return;
30854             }
30855             var r = this.findRecord(this.valueField, v);
30856             if(r){
30857                 sels.push(this.store.indexOf(r))
30858                 
30859             }
30860         },this);
30861         this.view.select(sels);
30862         return false;
30863     },
30864     
30865     
30866     
30867     onSelect : function(record, index){
30868        // Roo.log("onselect Called");
30869        // this is only called by the clear button now..
30870         this.view.clearSelections();
30871         this.setValue('[]');
30872         if (this.value != this.valueBefore) {
30873             this.fireEvent('change', this, this.value, this.valueBefore);
30874             this.valueBefore = this.value;
30875         }
30876     },
30877     getValueArray : function()
30878     {
30879         var ar = [] ;
30880         
30881         try {
30882             //Roo.log(this.value);
30883             if (typeof(this.value) == 'undefined') {
30884                 return [];
30885             }
30886             var ar = Roo.decode(this.value);
30887             return  ar instanceof Array ? ar : []; //?? valid?
30888             
30889         } catch(e) {
30890             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30891             return [];
30892         }
30893          
30894     },
30895     expand : function ()
30896     {
30897         
30898         Roo.form.ComboCheck.superclass.expand.call(this);
30899         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30900         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30901         
30902
30903     },
30904     
30905     collapse : function(){
30906         Roo.form.ComboCheck.superclass.collapse.call(this);
30907         var sl = this.view.getSelectedIndexes();
30908         var st = this.store;
30909         var nv = [];
30910         var tv = [];
30911         var r;
30912         Roo.each(sl, function(i) {
30913             r = st.getAt(i);
30914             nv.push(r.get(this.valueField));
30915         },this);
30916         this.setValue(Roo.encode(nv));
30917         if (this.value != this.valueBefore) {
30918
30919             this.fireEvent('change', this, this.value, this.valueBefore);
30920             this.valueBefore = this.value;
30921         }
30922         
30923     },
30924     
30925     setValue : function(v){
30926         // Roo.log(v);
30927         this.value = v;
30928         
30929         var vals = this.getValueArray();
30930         var tv = [];
30931         Roo.each(vals, function(k) {
30932             var r = this.findRecord(this.valueField, k);
30933             if(r){
30934                 tv.push(r.data[this.displayField]);
30935             }else if(this.valueNotFoundText !== undefined){
30936                 tv.push( this.valueNotFoundText );
30937             }
30938         },this);
30939        // Roo.log(tv);
30940         
30941         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30942         this.hiddenField.value = v;
30943         this.value = v;
30944     }
30945     
30946 });/*
30947  * Based on:
30948  * Ext JS Library 1.1.1
30949  * Copyright(c) 2006-2007, Ext JS, LLC.
30950  *
30951  * Originally Released Under LGPL - original licence link has changed is not relivant.
30952  *
30953  * Fork - LGPL
30954  * <script type="text/javascript">
30955  */
30956  
30957 /**
30958  * @class Roo.form.Signature
30959  * @extends Roo.form.Field
30960  * Signature field.  
30961  * @constructor
30962  * 
30963  * @param {Object} config Configuration options
30964  */
30965
30966 Roo.form.Signature = function(config){
30967     Roo.form.Signature.superclass.constructor.call(this, config);
30968     
30969     this.addEvents({// not in used??
30970          /**
30971          * @event confirm
30972          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30973              * @param {Roo.form.Signature} combo This combo box
30974              */
30975         'confirm' : true,
30976         /**
30977          * @event reset
30978          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30979              * @param {Roo.form.ComboBox} combo This combo box
30980              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30981              */
30982         'reset' : true
30983     });
30984 };
30985
30986 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30987     /**
30988      * @cfg {Object} labels Label to use when rendering a form.
30989      * defaults to 
30990      * labels : { 
30991      *      clear : "Clear",
30992      *      confirm : "Confirm"
30993      *  }
30994      */
30995     labels : { 
30996         clear : "Clear",
30997         confirm : "Confirm"
30998     },
30999     /**
31000      * @cfg {Number} width The signature panel width (defaults to 300)
31001      */
31002     width: 300,
31003     /**
31004      * @cfg {Number} height The signature panel height (defaults to 100)
31005      */
31006     height : 100,
31007     /**
31008      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31009      */
31010     allowBlank : false,
31011     
31012     //private
31013     // {Object} signPanel The signature SVG panel element (defaults to {})
31014     signPanel : {},
31015     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31016     isMouseDown : false,
31017     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31018     isConfirmed : false,
31019     // {String} signatureTmp SVG mapping string (defaults to empty string)
31020     signatureTmp : '',
31021     
31022     
31023     defaultAutoCreate : { // modified by initCompnoent..
31024         tag: "input",
31025         type:"hidden"
31026     },
31027
31028     // private
31029     onRender : function(ct, position){
31030         
31031         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31032         
31033         this.wrap = this.el.wrap({
31034             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31035         });
31036         
31037         this.createToolbar(this);
31038         this.signPanel = this.wrap.createChild({
31039                 tag: 'div',
31040                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31041             }, this.el
31042         );
31043             
31044         this.svgID = Roo.id();
31045         this.svgEl = this.signPanel.createChild({
31046               xmlns : 'http://www.w3.org/2000/svg',
31047               tag : 'svg',
31048               id : this.svgID + "-svg",
31049               width: this.width,
31050               height: this.height,
31051               viewBox: '0 0 '+this.width+' '+this.height,
31052               cn : [
31053                 {
31054                     tag: "rect",
31055                     id: this.svgID + "-svg-r",
31056                     width: this.width,
31057                     height: this.height,
31058                     fill: "#ffa"
31059                 },
31060                 {
31061                     tag: "line",
31062                     id: this.svgID + "-svg-l",
31063                     x1: "0", // start
31064                     y1: (this.height*0.8), // start set the line in 80% of height
31065                     x2: this.width, // end
31066                     y2: (this.height*0.8), // end set the line in 80% of height
31067                     'stroke': "#666",
31068                     'stroke-width': "1",
31069                     'stroke-dasharray': "3",
31070                     'shape-rendering': "crispEdges",
31071                     'pointer-events': "none"
31072                 },
31073                 {
31074                     tag: "path",
31075                     id: this.svgID + "-svg-p",
31076                     'stroke': "navy",
31077                     'stroke-width': "3",
31078                     'fill': "none",
31079                     'pointer-events': 'none'
31080                 }
31081               ]
31082         });
31083         this.createSVG();
31084         this.svgBox = this.svgEl.dom.getScreenCTM();
31085     },
31086     createSVG : function(){ 
31087         var svg = this.signPanel;
31088         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31089         var t = this;
31090
31091         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31092         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31093         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31094         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31095         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31096         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31097         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31098         
31099     },
31100     isTouchEvent : function(e){
31101         return e.type.match(/^touch/);
31102     },
31103     getCoords : function (e) {
31104         var pt    = this.svgEl.dom.createSVGPoint();
31105         pt.x = e.clientX; 
31106         pt.y = e.clientY;
31107         if (this.isTouchEvent(e)) {
31108             pt.x =  e.targetTouches[0].clientX 
31109             pt.y = e.targetTouches[0].clientY;
31110         }
31111         var a = this.svgEl.dom.getScreenCTM();
31112         var b = a.inverse();
31113         var mx = pt.matrixTransform(b);
31114         return mx.x + ',' + mx.y;
31115     },
31116     //mouse event headler 
31117     down : function (e) {
31118         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31119         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31120         
31121         this.isMouseDown = true;
31122         
31123         e.preventDefault();
31124     },
31125     move : function (e) {
31126         if (this.isMouseDown) {
31127             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31128             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31129         }
31130         
31131         e.preventDefault();
31132     },
31133     up : function (e) {
31134         this.isMouseDown = false;
31135         var sp = this.signatureTmp.split(' ');
31136         
31137         if(sp.length > 1){
31138             if(!sp[sp.length-2].match(/^L/)){
31139                 sp.pop();
31140                 sp.pop();
31141                 sp.push("");
31142                 this.signatureTmp = sp.join(" ");
31143             }
31144         }
31145         if(this.getValue() != this.signatureTmp){
31146             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31147             this.isConfirmed = false;
31148         }
31149         e.preventDefault();
31150     },
31151     
31152     /**
31153      * Protected method that will not generally be called directly. It
31154      * is called when the editor creates its toolbar. Override this method if you need to
31155      * add custom toolbar buttons.
31156      * @param {HtmlEditor} editor
31157      */
31158     createToolbar : function(editor){
31159          function btn(id, toggle, handler){
31160             var xid = fid + '-'+ id ;
31161             return {
31162                 id : xid,
31163                 cmd : id,
31164                 cls : 'x-btn-icon x-edit-'+id,
31165                 enableToggle:toggle !== false,
31166                 scope: editor, // was editor...
31167                 handler:handler||editor.relayBtnCmd,
31168                 clickEvent:'mousedown',
31169                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31170                 tabIndex:-1
31171             };
31172         }
31173         
31174         
31175         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31176         this.tb = tb;
31177         this.tb.add(
31178            {
31179                 cls : ' x-signature-btn x-signature-'+id,
31180                 scope: editor, // was editor...
31181                 handler: this.reset,
31182                 clickEvent:'mousedown',
31183                 text: this.labels.clear
31184             },
31185             {
31186                  xtype : 'Fill',
31187                  xns: Roo.Toolbar
31188             }, 
31189             {
31190                 cls : '  x-signature-btn x-signature-'+id,
31191                 scope: editor, // was editor...
31192                 handler: this.confirmHandler,
31193                 clickEvent:'mousedown',
31194                 text: this.labels.confirm
31195             }
31196         );
31197     
31198     },
31199     //public
31200     /**
31201      * when user is clicked confirm then show this image.....
31202      * 
31203      * @return {String} Image Data URI
31204      */
31205     getImageDataURI : function(){
31206         var svg = this.svgEl.dom.parentNode.innerHTML;
31207         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31208         return src; 
31209     },
31210     /**
31211      * 
31212      * @return {Boolean} this.isConfirmed
31213      */
31214     getConfirmed : function(){
31215         return this.isConfirmed;
31216     },
31217     /**
31218      * 
31219      * @return {Number} this.width
31220      */
31221     getWidth : function(){
31222         return this.width;
31223     },
31224     /**
31225      * 
31226      * @return {Number} this.height
31227      */
31228     getHeight : function(){
31229         return this.height;
31230     },
31231     // private
31232     getSignature : function(){
31233         return this.signatureTmp;
31234     },
31235     // private
31236     reset : function(){
31237         this.signatureTmp = '';
31238         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31239         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31240         this.isConfirmed = false;
31241         Roo.form.Signature.superclass.reset.call(this);
31242     },
31243     setSignature : function(s){
31244         this.signatureTmp = s;
31245         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31246         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31247         this.setValue(s);
31248         this.isConfirmed = false;
31249         Roo.form.Signature.superclass.reset.call(this);
31250     }, 
31251     test : function(){
31252 //        Roo.log(this.signPanel.dom.contentWindow.up())
31253     },
31254     //private
31255     setConfirmed : function(){
31256         
31257         
31258         
31259 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31260     },
31261     // private
31262     confirmHandler : function(){
31263         if(!this.getSignature()){
31264             return;
31265         }
31266         
31267         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31268         this.setValue(this.getSignature());
31269         this.isConfirmed = true;
31270         
31271         this.fireEvent('confirm', this);
31272     },
31273     // private
31274     // Subclasses should provide the validation implementation by overriding this
31275     validateValue : function(value){
31276         if(this.allowBlank){
31277             return true;
31278         }
31279         
31280         if(this.isConfirmed){
31281             return true;
31282         }
31283         return false;
31284     }
31285 });/*
31286  * Based on:
31287  * Ext JS Library 1.1.1
31288  * Copyright(c) 2006-2007, Ext JS, LLC.
31289  *
31290  * Originally Released Under LGPL - original licence link has changed is not relivant.
31291  *
31292  * Fork - LGPL
31293  * <script type="text/javascript">
31294  */
31295  
31296
31297 /**
31298  * @class Roo.form.ComboBox
31299  * @extends Roo.form.TriggerField
31300  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31301  * @constructor
31302  * Create a new ComboBox.
31303  * @param {Object} config Configuration options
31304  */
31305 Roo.form.Select = function(config){
31306     Roo.form.Select.superclass.constructor.call(this, config);
31307      
31308 };
31309
31310 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31311     /**
31312      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31313      */
31314     /**
31315      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31316      * rendering into an Roo.Editor, defaults to false)
31317      */
31318     /**
31319      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31320      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31321      */
31322     /**
31323      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31324      */
31325     /**
31326      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31327      * the dropdown list (defaults to undefined, with no header element)
31328      */
31329
31330      /**
31331      * @cfg {String/Roo.Template} tpl The template to use to render the output
31332      */
31333      
31334     // private
31335     defaultAutoCreate : {tag: "select"  },
31336     /**
31337      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31338      */
31339     listWidth: undefined,
31340     /**
31341      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31342      * mode = 'remote' or 'text' if mode = 'local')
31343      */
31344     displayField: undefined,
31345     /**
31346      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31347      * mode = 'remote' or 'value' if mode = 'local'). 
31348      * Note: use of a valueField requires the user make a selection
31349      * in order for a value to be mapped.
31350      */
31351     valueField: undefined,
31352     
31353     
31354     /**
31355      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31356      * field's data value (defaults to the underlying DOM element's name)
31357      */
31358     hiddenName: undefined,
31359     /**
31360      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31361      */
31362     listClass: '',
31363     /**
31364      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31365      */
31366     selectedClass: 'x-combo-selected',
31367     /**
31368      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31369      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31370      * which displays a downward arrow icon).
31371      */
31372     triggerClass : 'x-form-arrow-trigger',
31373     /**
31374      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31375      */
31376     shadow:'sides',
31377     /**
31378      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31379      * anchor positions (defaults to 'tl-bl')
31380      */
31381     listAlign: 'tl-bl?',
31382     /**
31383      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31384      */
31385     maxHeight: 300,
31386     /**
31387      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31388      * query specified by the allQuery config option (defaults to 'query')
31389      */
31390     triggerAction: 'query',
31391     /**
31392      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31393      * (defaults to 4, does not apply if editable = false)
31394      */
31395     minChars : 4,
31396     /**
31397      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31398      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31399      */
31400     typeAhead: false,
31401     /**
31402      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31403      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31404      */
31405     queryDelay: 500,
31406     /**
31407      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31408      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31409      */
31410     pageSize: 0,
31411     /**
31412      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31413      * when editable = true (defaults to false)
31414      */
31415     selectOnFocus:false,
31416     /**
31417      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31418      */
31419     queryParam: 'query',
31420     /**
31421      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31422      * when mode = 'remote' (defaults to 'Loading...')
31423      */
31424     loadingText: 'Loading...',
31425     /**
31426      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31427      */
31428     resizable: false,
31429     /**
31430      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31431      */
31432     handleHeight : 8,
31433     /**
31434      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31435      * traditional select (defaults to true)
31436      */
31437     editable: true,
31438     /**
31439      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31440      */
31441     allQuery: '',
31442     /**
31443      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31444      */
31445     mode: 'remote',
31446     /**
31447      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31448      * listWidth has a higher value)
31449      */
31450     minListWidth : 70,
31451     /**
31452      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31453      * allow the user to set arbitrary text into the field (defaults to false)
31454      */
31455     forceSelection:false,
31456     /**
31457      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31458      * if typeAhead = true (defaults to 250)
31459      */
31460     typeAheadDelay : 250,
31461     /**
31462      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31463      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31464      */
31465     valueNotFoundText : undefined,
31466     
31467     /**
31468      * @cfg {String} defaultValue The value displayed after loading the store.
31469      */
31470     defaultValue: '',
31471     
31472     /**
31473      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31474      */
31475     blockFocus : false,
31476     
31477     /**
31478      * @cfg {Boolean} disableClear Disable showing of clear button.
31479      */
31480     disableClear : false,
31481     /**
31482      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31483      */
31484     alwaysQuery : false,
31485     
31486     //private
31487     addicon : false,
31488     editicon: false,
31489     
31490     // element that contains real text value.. (when hidden is used..)
31491      
31492     // private
31493     onRender : function(ct, position){
31494         Roo.form.Field.prototype.onRender.call(this, ct, position);
31495         
31496         if(this.store){
31497             this.store.on('beforeload', this.onBeforeLoad, this);
31498             this.store.on('load', this.onLoad, this);
31499             this.store.on('loadexception', this.onLoadException, this);
31500             this.store.load({});
31501         }
31502         
31503         
31504         
31505     },
31506
31507     // private
31508     initEvents : function(){
31509         //Roo.form.ComboBox.superclass.initEvents.call(this);
31510  
31511     },
31512
31513     onDestroy : function(){
31514        
31515         if(this.store){
31516             this.store.un('beforeload', this.onBeforeLoad, this);
31517             this.store.un('load', this.onLoad, this);
31518             this.store.un('loadexception', this.onLoadException, this);
31519         }
31520         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31521     },
31522
31523     // private
31524     fireKey : function(e){
31525         if(e.isNavKeyPress() && !this.list.isVisible()){
31526             this.fireEvent("specialkey", this, e);
31527         }
31528     },
31529
31530     // private
31531     onResize: function(w, h){
31532         
31533         return; 
31534     
31535         
31536     },
31537
31538     /**
31539      * Allow or prevent the user from directly editing the field text.  If false is passed,
31540      * the user will only be able to select from the items defined in the dropdown list.  This method
31541      * is the runtime equivalent of setting the 'editable' config option at config time.
31542      * @param {Boolean} value True to allow the user to directly edit the field text
31543      */
31544     setEditable : function(value){
31545          
31546     },
31547
31548     // private
31549     onBeforeLoad : function(){
31550         
31551         Roo.log("Select before load");
31552         return;
31553     
31554         this.innerList.update(this.loadingText ?
31555                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31556         //this.restrictHeight();
31557         this.selectedIndex = -1;
31558     },
31559
31560     // private
31561     onLoad : function(){
31562
31563     
31564         var dom = this.el.dom;
31565         dom.innerHTML = '';
31566          var od = dom.ownerDocument;
31567          
31568         if (this.emptyText) {
31569             var op = od.createElement('option');
31570             op.setAttribute('value', '');
31571             op.innerHTML = String.format('{0}', this.emptyText);
31572             dom.appendChild(op);
31573         }
31574         if(this.store.getCount() > 0){
31575            
31576             var vf = this.valueField;
31577             var df = this.displayField;
31578             this.store.data.each(function(r) {
31579                 // which colmsn to use... testing - cdoe / title..
31580                 var op = od.createElement('option');
31581                 op.setAttribute('value', r.data[vf]);
31582                 op.innerHTML = String.format('{0}', r.data[df]);
31583                 dom.appendChild(op);
31584             });
31585             if (typeof(this.defaultValue != 'undefined')) {
31586                 this.setValue(this.defaultValue);
31587             }
31588             
31589              
31590         }else{
31591             //this.onEmptyResults();
31592         }
31593         //this.el.focus();
31594     },
31595     // private
31596     onLoadException : function()
31597     {
31598         dom.innerHTML = '';
31599             
31600         Roo.log("Select on load exception");
31601         return;
31602     
31603         this.collapse();
31604         Roo.log(this.store.reader.jsonData);
31605         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31606             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31607         }
31608         
31609         
31610     },
31611     // private
31612     onTypeAhead : function(){
31613          
31614     },
31615
31616     // private
31617     onSelect : function(record, index){
31618         Roo.log('on select?');
31619         return;
31620         if(this.fireEvent('beforeselect', this, record, index) !== false){
31621             this.setFromData(index > -1 ? record.data : false);
31622             this.collapse();
31623             this.fireEvent('select', this, record, index);
31624         }
31625     },
31626
31627     /**
31628      * Returns the currently selected field value or empty string if no value is set.
31629      * @return {String} value The selected value
31630      */
31631     getValue : function(){
31632         var dom = this.el.dom;
31633         this.value = dom.options[dom.selectedIndex].value;
31634         return this.value;
31635         
31636     },
31637
31638     /**
31639      * Clears any text/value currently set in the field
31640      */
31641     clearValue : function(){
31642         this.value = '';
31643         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31644         
31645     },
31646
31647     /**
31648      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31649      * will be displayed in the field.  If the value does not match the data value of an existing item,
31650      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31651      * Otherwise the field will be blank (although the value will still be set).
31652      * @param {String} value The value to match
31653      */
31654     setValue : function(v){
31655         var d = this.el.dom;
31656         for (var i =0; i < d.options.length;i++) {
31657             if (v == d.options[i].value) {
31658                 d.selectedIndex = i;
31659                 this.value = v;
31660                 return;
31661             }
31662         }
31663         this.clearValue();
31664     },
31665     /**
31666      * @property {Object} the last set data for the element
31667      */
31668     
31669     lastData : false,
31670     /**
31671      * Sets the value of the field based on a object which is related to the record format for the store.
31672      * @param {Object} value the value to set as. or false on reset?
31673      */
31674     setFromData : function(o){
31675         Roo.log('setfrom data?');
31676          
31677         
31678         
31679     },
31680     // private
31681     reset : function(){
31682         this.clearValue();
31683     },
31684     // private
31685     findRecord : function(prop, value){
31686         
31687         return false;
31688     
31689         var record;
31690         if(this.store.getCount() > 0){
31691             this.store.each(function(r){
31692                 if(r.data[prop] == value){
31693                     record = r;
31694                     return false;
31695                 }
31696                 return true;
31697             });
31698         }
31699         return record;
31700     },
31701     
31702     getName: function()
31703     {
31704         // returns hidden if it's set..
31705         if (!this.rendered) {return ''};
31706         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31707         
31708     },
31709      
31710
31711     
31712
31713     // private
31714     onEmptyResults : function(){
31715         Roo.log('empty results');
31716         //this.collapse();
31717     },
31718
31719     /**
31720      * Returns true if the dropdown list is expanded, else false.
31721      */
31722     isExpanded : function(){
31723         return false;
31724     },
31725
31726     /**
31727      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31728      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31729      * @param {String} value The data value of the item to select
31730      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31731      * selected item if it is not currently in view (defaults to true)
31732      * @return {Boolean} True if the value matched an item in the list, else false
31733      */
31734     selectByValue : function(v, scrollIntoView){
31735         Roo.log('select By Value');
31736         return false;
31737     
31738         if(v !== undefined && v !== null){
31739             var r = this.findRecord(this.valueField || this.displayField, v);
31740             if(r){
31741                 this.select(this.store.indexOf(r), scrollIntoView);
31742                 return true;
31743             }
31744         }
31745         return false;
31746     },
31747
31748     /**
31749      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31750      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31751      * @param {Number} index The zero-based index of the list item to select
31752      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31753      * selected item if it is not currently in view (defaults to true)
31754      */
31755     select : function(index, scrollIntoView){
31756         Roo.log('select ');
31757         return  ;
31758         
31759         this.selectedIndex = index;
31760         this.view.select(index);
31761         if(scrollIntoView !== false){
31762             var el = this.view.getNode(index);
31763             if(el){
31764                 this.innerList.scrollChildIntoView(el, false);
31765             }
31766         }
31767     },
31768
31769       
31770
31771     // private
31772     validateBlur : function(){
31773         
31774         return;
31775         
31776     },
31777
31778     // private
31779     initQuery : function(){
31780         this.doQuery(this.getRawValue());
31781     },
31782
31783     // private
31784     doForce : function(){
31785         if(this.el.dom.value.length > 0){
31786             this.el.dom.value =
31787                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31788              
31789         }
31790     },
31791
31792     /**
31793      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31794      * query allowing the query action to be canceled if needed.
31795      * @param {String} query The SQL query to execute
31796      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31797      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31798      * saved in the current store (defaults to false)
31799      */
31800     doQuery : function(q, forceAll){
31801         
31802         Roo.log('doQuery?');
31803         if(q === undefined || q === null){
31804             q = '';
31805         }
31806         var qe = {
31807             query: q,
31808             forceAll: forceAll,
31809             combo: this,
31810             cancel:false
31811         };
31812         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31813             return false;
31814         }
31815         q = qe.query;
31816         forceAll = qe.forceAll;
31817         if(forceAll === true || (q.length >= this.minChars)){
31818             if(this.lastQuery != q || this.alwaysQuery){
31819                 this.lastQuery = q;
31820                 if(this.mode == 'local'){
31821                     this.selectedIndex = -1;
31822                     if(forceAll){
31823                         this.store.clearFilter();
31824                     }else{
31825                         this.store.filter(this.displayField, q);
31826                     }
31827                     this.onLoad();
31828                 }else{
31829                     this.store.baseParams[this.queryParam] = q;
31830                     this.store.load({
31831                         params: this.getParams(q)
31832                     });
31833                     this.expand();
31834                 }
31835             }else{
31836                 this.selectedIndex = -1;
31837                 this.onLoad();   
31838             }
31839         }
31840     },
31841
31842     // private
31843     getParams : function(q){
31844         var p = {};
31845         //p[this.queryParam] = q;
31846         if(this.pageSize){
31847             p.start = 0;
31848             p.limit = this.pageSize;
31849         }
31850         return p;
31851     },
31852
31853     /**
31854      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31855      */
31856     collapse : function(){
31857         
31858     },
31859
31860     // private
31861     collapseIf : function(e){
31862         
31863     },
31864
31865     /**
31866      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31867      */
31868     expand : function(){
31869         
31870     } ,
31871
31872     // private
31873      
31874
31875     /** 
31876     * @cfg {Boolean} grow 
31877     * @hide 
31878     */
31879     /** 
31880     * @cfg {Number} growMin 
31881     * @hide 
31882     */
31883     /** 
31884     * @cfg {Number} growMax 
31885     * @hide 
31886     */
31887     /**
31888      * @hide
31889      * @method autoSize
31890      */
31891     
31892     setWidth : function()
31893     {
31894         
31895     },
31896     getResizeEl : function(){
31897         return this.el;
31898     }
31899 });//<script type="text/javasscript">
31900  
31901
31902 /**
31903  * @class Roo.DDView
31904  * A DnD enabled version of Roo.View.
31905  * @param {Element/String} container The Element in which to create the View.
31906  * @param {String} tpl The template string used to create the markup for each element of the View
31907  * @param {Object} config The configuration properties. These include all the config options of
31908  * {@link Roo.View} plus some specific to this class.<br>
31909  * <p>
31910  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31911  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31912  * <p>
31913  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31914 .x-view-drag-insert-above {
31915         border-top:1px dotted #3366cc;
31916 }
31917 .x-view-drag-insert-below {
31918         border-bottom:1px dotted #3366cc;
31919 }
31920 </code></pre>
31921  * 
31922  */
31923  
31924 Roo.DDView = function(container, tpl, config) {
31925     Roo.DDView.superclass.constructor.apply(this, arguments);
31926     this.getEl().setStyle("outline", "0px none");
31927     this.getEl().unselectable();
31928     if (this.dragGroup) {
31929                 this.setDraggable(this.dragGroup.split(","));
31930     }
31931     if (this.dropGroup) {
31932                 this.setDroppable(this.dropGroup.split(","));
31933     }
31934     if (this.deletable) {
31935         this.setDeletable();
31936     }
31937     this.isDirtyFlag = false;
31938         this.addEvents({
31939                 "drop" : true
31940         });
31941 };
31942
31943 Roo.extend(Roo.DDView, Roo.View, {
31944 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31945 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31946 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31947 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31948
31949         isFormField: true,
31950
31951         reset: Roo.emptyFn,
31952         
31953         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31954
31955         validate: function() {
31956                 return true;
31957         },
31958         
31959         destroy: function() {
31960                 this.purgeListeners();
31961                 this.getEl.removeAllListeners();
31962                 this.getEl().remove();
31963                 if (this.dragZone) {
31964                         if (this.dragZone.destroy) {
31965                                 this.dragZone.destroy();
31966                         }
31967                 }
31968                 if (this.dropZone) {
31969                         if (this.dropZone.destroy) {
31970                                 this.dropZone.destroy();
31971                         }
31972                 }
31973         },
31974
31975 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31976         getName: function() {
31977                 return this.name;
31978         },
31979
31980 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31981         setValue: function(v) {
31982                 if (!this.store) {
31983                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31984                 }
31985                 var data = {};
31986                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31987                 this.store.proxy = new Roo.data.MemoryProxy(data);
31988                 this.store.load();
31989         },
31990
31991 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31992         getValue: function() {
31993                 var result = '(';
31994                 this.store.each(function(rec) {
31995                         result += rec.id + ',';
31996                 });
31997                 return result.substr(0, result.length - 1) + ')';
31998         },
31999         
32000         getIds: function() {
32001                 var i = 0, result = new Array(this.store.getCount());
32002                 this.store.each(function(rec) {
32003                         result[i++] = rec.id;
32004                 });
32005                 return result;
32006         },
32007         
32008         isDirty: function() {
32009                 return this.isDirtyFlag;
32010         },
32011
32012 /**
32013  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32014  *      whole Element becomes the target, and this causes the drop gesture to append.
32015  */
32016     getTargetFromEvent : function(e) {
32017                 var target = e.getTarget();
32018                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32019                 target = target.parentNode;
32020                 }
32021                 if (!target) {
32022                         target = this.el.dom.lastChild || this.el.dom;
32023                 }
32024                 return target;
32025     },
32026
32027 /**
32028  *      Create the drag data which consists of an object which has the property "ddel" as
32029  *      the drag proxy element. 
32030  */
32031     getDragData : function(e) {
32032         var target = this.findItemFromChild(e.getTarget());
32033                 if(target) {
32034                         this.handleSelection(e);
32035                         var selNodes = this.getSelectedNodes();
32036             var dragData = {
32037                 source: this,
32038                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32039                 nodes: selNodes,
32040                 records: []
32041                         };
32042                         var selectedIndices = this.getSelectedIndexes();
32043                         for (var i = 0; i < selectedIndices.length; i++) {
32044                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32045                         }
32046                         if (selNodes.length == 1) {
32047                                 dragData.ddel = target.cloneNode(true); // the div element
32048                         } else {
32049                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32050                                 div.className = 'multi-proxy';
32051                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32052                                         div.appendChild(selNodes[i].cloneNode(true));
32053                                 }
32054                                 dragData.ddel = div;
32055                         }
32056             //console.log(dragData)
32057             //console.log(dragData.ddel.innerHTML)
32058                         return dragData;
32059                 }
32060         //console.log('nodragData')
32061                 return false;
32062     },
32063     
32064 /**     Specify to which ddGroup items in this DDView may be dragged. */
32065     setDraggable: function(ddGroup) {
32066         if (ddGroup instanceof Array) {
32067                 Roo.each(ddGroup, this.setDraggable, this);
32068                 return;
32069         }
32070         if (this.dragZone) {
32071                 this.dragZone.addToGroup(ddGroup);
32072         } else {
32073                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32074                                 containerScroll: true,
32075                                 ddGroup: ddGroup 
32076
32077                         });
32078 //                      Draggability implies selection. DragZone's mousedown selects the element.
32079                         if (!this.multiSelect) { this.singleSelect = true; }
32080
32081 //                      Wire the DragZone's handlers up to methods in *this*
32082                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32083                 }
32084     },
32085
32086 /**     Specify from which ddGroup this DDView accepts drops. */
32087     setDroppable: function(ddGroup) {
32088         if (ddGroup instanceof Array) {
32089                 Roo.each(ddGroup, this.setDroppable, this);
32090                 return;
32091         }
32092         if (this.dropZone) {
32093                 this.dropZone.addToGroup(ddGroup);
32094         } else {
32095                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32096                                 containerScroll: true,
32097                                 ddGroup: ddGroup
32098                         });
32099
32100 //                      Wire the DropZone's handlers up to methods in *this*
32101                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32102                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32103                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32104                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32105                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32106                 }
32107     },
32108
32109 /**     Decide whether to drop above or below a View node. */
32110     getDropPoint : function(e, n, dd){
32111         if (n == this.el.dom) { return "above"; }
32112                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32113                 var c = t + (b - t) / 2;
32114                 var y = Roo.lib.Event.getPageY(e);
32115                 if(y <= c) {
32116                         return "above";
32117                 }else{
32118                         return "below";
32119                 }
32120     },
32121
32122     onNodeEnter : function(n, dd, e, data){
32123                 return false;
32124     },
32125     
32126     onNodeOver : function(n, dd, e, data){
32127                 var pt = this.getDropPoint(e, n, dd);
32128                 // set the insert point style on the target node
32129                 var dragElClass = this.dropNotAllowed;
32130                 if (pt) {
32131                         var targetElClass;
32132                         if (pt == "above"){
32133                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32134                                 targetElClass = "x-view-drag-insert-above";
32135                         } else {
32136                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32137                                 targetElClass = "x-view-drag-insert-below";
32138                         }
32139                         if (this.lastInsertClass != targetElClass){
32140                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32141                                 this.lastInsertClass = targetElClass;
32142                         }
32143                 }
32144                 return dragElClass;
32145         },
32146
32147     onNodeOut : function(n, dd, e, data){
32148                 this.removeDropIndicators(n);
32149     },
32150
32151     onNodeDrop : function(n, dd, e, data){
32152         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32153                 return false;
32154         }
32155         var pt = this.getDropPoint(e, n, dd);
32156                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32157                 if (pt == "below") { insertAt++; }
32158                 for (var i = 0; i < data.records.length; i++) {
32159                         var r = data.records[i];
32160                         var dup = this.store.getById(r.id);
32161                         if (dup && (dd != this.dragZone)) {
32162                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32163                         } else {
32164                                 if (data.copy) {
32165                                         this.store.insert(insertAt++, r.copy());
32166                                 } else {
32167                                         data.source.isDirtyFlag = true;
32168                                         r.store.remove(r);
32169                                         this.store.insert(insertAt++, r);
32170                                 }
32171                                 this.isDirtyFlag = true;
32172                         }
32173                 }
32174                 this.dragZone.cachedTarget = null;
32175                 return true;
32176     },
32177
32178     removeDropIndicators : function(n){
32179                 if(n){
32180                         Roo.fly(n).removeClass([
32181                                 "x-view-drag-insert-above",
32182                                 "x-view-drag-insert-below"]);
32183                         this.lastInsertClass = "_noclass";
32184                 }
32185     },
32186
32187 /**
32188  *      Utility method. Add a delete option to the DDView's context menu.
32189  *      @param {String} imageUrl The URL of the "delete" icon image.
32190  */
32191         setDeletable: function(imageUrl) {
32192                 if (!this.singleSelect && !this.multiSelect) {
32193                         this.singleSelect = true;
32194                 }
32195                 var c = this.getContextMenu();
32196                 this.contextMenu.on("itemclick", function(item) {
32197                         switch (item.id) {
32198                                 case "delete":
32199                                         this.remove(this.getSelectedIndexes());
32200                                         break;
32201                         }
32202                 }, this);
32203                 this.contextMenu.add({
32204                         icon: imageUrl,
32205                         id: "delete",
32206                         text: 'Delete'
32207                 });
32208         },
32209         
32210 /**     Return the context menu for this DDView. */
32211         getContextMenu: function() {
32212                 if (!this.contextMenu) {
32213 //                      Create the View's context menu
32214                         this.contextMenu = new Roo.menu.Menu({
32215                                 id: this.id + "-contextmenu"
32216                         });
32217                         this.el.on("contextmenu", this.showContextMenu, this);
32218                 }
32219                 return this.contextMenu;
32220         },
32221         
32222         disableContextMenu: function() {
32223                 if (this.contextMenu) {
32224                         this.el.un("contextmenu", this.showContextMenu, this);
32225                 }
32226         },
32227
32228         showContextMenu: function(e, item) {
32229         item = this.findItemFromChild(e.getTarget());
32230                 if (item) {
32231                         e.stopEvent();
32232                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32233                         this.contextMenu.showAt(e.getXY());
32234             }
32235     },
32236
32237 /**
32238  *      Remove {@link Roo.data.Record}s at the specified indices.
32239  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32240  */
32241     remove: function(selectedIndices) {
32242                 selectedIndices = [].concat(selectedIndices);
32243                 for (var i = 0; i < selectedIndices.length; i++) {
32244                         var rec = this.store.getAt(selectedIndices[i]);
32245                         this.store.remove(rec);
32246                 }
32247     },
32248
32249 /**
32250  *      Double click fires the event, but also, if this is draggable, and there is only one other
32251  *      related DropZone, it transfers the selected node.
32252  */
32253     onDblClick : function(e){
32254         var item = this.findItemFromChild(e.getTarget());
32255         if(item){
32256             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32257                 return false;
32258             }
32259             if (this.dragGroup) {
32260                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32261                     while (targets.indexOf(this.dropZone) > -1) {
32262                             targets.remove(this.dropZone);
32263                                 }
32264                     if (targets.length == 1) {
32265                                         this.dragZone.cachedTarget = null;
32266                         var el = Roo.get(targets[0].getEl());
32267                         var box = el.getBox(true);
32268                         targets[0].onNodeDrop(el.dom, {
32269                                 target: el.dom,
32270                                 xy: [box.x, box.y + box.height - 1]
32271                         }, null, this.getDragData(e));
32272                     }
32273                 }
32274         }
32275     },
32276     
32277     handleSelection: function(e) {
32278                 this.dragZone.cachedTarget = null;
32279         var item = this.findItemFromChild(e.getTarget());
32280         if (!item) {
32281                 this.clearSelections(true);
32282                 return;
32283         }
32284                 if (item && (this.multiSelect || this.singleSelect)){
32285                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32286                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32287                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32288                                 this.unselect(item);
32289                         } else {
32290                                 this.select(item, this.multiSelect && e.ctrlKey);
32291                                 this.lastSelection = item;
32292                         }
32293                 }
32294     },
32295
32296     onItemClick : function(item, index, e){
32297                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32298                         return false;
32299                 }
32300                 return true;
32301     },
32302
32303     unselect : function(nodeInfo, suppressEvent){
32304                 var node = this.getNode(nodeInfo);
32305                 if(node && this.isSelected(node)){
32306                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32307                                 Roo.fly(node).removeClass(this.selectedClass);
32308                                 this.selections.remove(node);
32309                                 if(!suppressEvent){
32310                                         this.fireEvent("selectionchange", this, this.selections);
32311                                 }
32312                         }
32313                 }
32314     }
32315 });
32316 /*
32317  * Based on:
32318  * Ext JS Library 1.1.1
32319  * Copyright(c) 2006-2007, Ext JS, LLC.
32320  *
32321  * Originally Released Under LGPL - original licence link has changed is not relivant.
32322  *
32323  * Fork - LGPL
32324  * <script type="text/javascript">
32325  */
32326  
32327 /**
32328  * @class Roo.LayoutManager
32329  * @extends Roo.util.Observable
32330  * Base class for layout managers.
32331  */
32332 Roo.LayoutManager = function(container, config){
32333     Roo.LayoutManager.superclass.constructor.call(this);
32334     this.el = Roo.get(container);
32335     // ie scrollbar fix
32336     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32337         document.body.scroll = "no";
32338     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32339         this.el.position('relative');
32340     }
32341     this.id = this.el.id;
32342     this.el.addClass("x-layout-container");
32343     /** false to disable window resize monitoring @type Boolean */
32344     this.monitorWindowResize = true;
32345     this.regions = {};
32346     this.addEvents({
32347         /**
32348          * @event layout
32349          * Fires when a layout is performed. 
32350          * @param {Roo.LayoutManager} this
32351          */
32352         "layout" : true,
32353         /**
32354          * @event regionresized
32355          * Fires when the user resizes a region. 
32356          * @param {Roo.LayoutRegion} region The resized region
32357          * @param {Number} newSize The new size (width for east/west, height for north/south)
32358          */
32359         "regionresized" : true,
32360         /**
32361          * @event regioncollapsed
32362          * Fires when a region is collapsed. 
32363          * @param {Roo.LayoutRegion} region The collapsed region
32364          */
32365         "regioncollapsed" : true,
32366         /**
32367          * @event regionexpanded
32368          * Fires when a region is expanded.  
32369          * @param {Roo.LayoutRegion} region The expanded region
32370          */
32371         "regionexpanded" : true
32372     });
32373     this.updating = false;
32374     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32375 };
32376
32377 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32378     /**
32379      * Returns true if this layout is currently being updated
32380      * @return {Boolean}
32381      */
32382     isUpdating : function(){
32383         return this.updating; 
32384     },
32385     
32386     /**
32387      * Suspend the LayoutManager from doing auto-layouts while
32388      * making multiple add or remove calls
32389      */
32390     beginUpdate : function(){
32391         this.updating = true;    
32392     },
32393     
32394     /**
32395      * Restore auto-layouts and optionally disable the manager from performing a layout
32396      * @param {Boolean} noLayout true to disable a layout update 
32397      */
32398     endUpdate : function(noLayout){
32399         this.updating = false;
32400         if(!noLayout){
32401             this.layout();
32402         }    
32403     },
32404     
32405     layout: function(){
32406         
32407     },
32408     
32409     onRegionResized : function(region, newSize){
32410         this.fireEvent("regionresized", region, newSize);
32411         this.layout();
32412     },
32413     
32414     onRegionCollapsed : function(region){
32415         this.fireEvent("regioncollapsed", region);
32416     },
32417     
32418     onRegionExpanded : function(region){
32419         this.fireEvent("regionexpanded", region);
32420     },
32421         
32422     /**
32423      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32424      * performs box-model adjustments.
32425      * @return {Object} The size as an object {width: (the width), height: (the height)}
32426      */
32427     getViewSize : function(){
32428         var size;
32429         if(this.el.dom != document.body){
32430             size = this.el.getSize();
32431         }else{
32432             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32433         }
32434         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32435         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32436         return size;
32437     },
32438     
32439     /**
32440      * Returns the Element this layout is bound to.
32441      * @return {Roo.Element}
32442      */
32443     getEl : function(){
32444         return this.el;
32445     },
32446     
32447     /**
32448      * Returns the specified region.
32449      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32450      * @return {Roo.LayoutRegion}
32451      */
32452     getRegion : function(target){
32453         return this.regions[target.toLowerCase()];
32454     },
32455     
32456     onWindowResize : function(){
32457         if(this.monitorWindowResize){
32458             this.layout();
32459         }
32460     }
32461 });/*
32462  * Based on:
32463  * Ext JS Library 1.1.1
32464  * Copyright(c) 2006-2007, Ext JS, LLC.
32465  *
32466  * Originally Released Under LGPL - original licence link has changed is not relivant.
32467  *
32468  * Fork - LGPL
32469  * <script type="text/javascript">
32470  */
32471 /**
32472  * @class Roo.BorderLayout
32473  * @extends Roo.LayoutManager
32474  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32475  * please see: <br><br>
32476  * <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>
32477  * <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>
32478  * Example:
32479  <pre><code>
32480  var layout = new Roo.BorderLayout(document.body, {
32481     north: {
32482         initialSize: 25,
32483         titlebar: false
32484     },
32485     west: {
32486         split:true,
32487         initialSize: 200,
32488         minSize: 175,
32489         maxSize: 400,
32490         titlebar: true,
32491         collapsible: true
32492     },
32493     east: {
32494         split:true,
32495         initialSize: 202,
32496         minSize: 175,
32497         maxSize: 400,
32498         titlebar: true,
32499         collapsible: true
32500     },
32501     south: {
32502         split:true,
32503         initialSize: 100,
32504         minSize: 100,
32505         maxSize: 200,
32506         titlebar: true,
32507         collapsible: true
32508     },
32509     center: {
32510         titlebar: true,
32511         autoScroll:true,
32512         resizeTabs: true,
32513         minTabWidth: 50,
32514         preferredTabWidth: 150
32515     }
32516 });
32517
32518 // shorthand
32519 var CP = Roo.ContentPanel;
32520
32521 layout.beginUpdate();
32522 layout.add("north", new CP("north", "North"));
32523 layout.add("south", new CP("south", {title: "South", closable: true}));
32524 layout.add("west", new CP("west", {title: "West"}));
32525 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32526 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32527 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32528 layout.getRegion("center").showPanel("center1");
32529 layout.endUpdate();
32530 </code></pre>
32531
32532 <b>The container the layout is rendered into can be either the body element or any other element.
32533 If it is not the body element, the container needs to either be an absolute positioned element,
32534 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32535 the container size if it is not the body element.</b>
32536
32537 * @constructor
32538 * Create a new BorderLayout
32539 * @param {String/HTMLElement/Element} container The container this layout is bound to
32540 * @param {Object} config Configuration options
32541  */
32542 Roo.BorderLayout = function(container, config){
32543     config = config || {};
32544     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32545     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32546     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32547         var target = this.factory.validRegions[i];
32548         if(config[target]){
32549             this.addRegion(target, config[target]);
32550         }
32551     }
32552 };
32553
32554 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32555     /**
32556      * Creates and adds a new region if it doesn't already exist.
32557      * @param {String} target The target region key (north, south, east, west or center).
32558      * @param {Object} config The regions config object
32559      * @return {BorderLayoutRegion} The new region
32560      */
32561     addRegion : function(target, config){
32562         if(!this.regions[target]){
32563             var r = this.factory.create(target, this, config);
32564             this.bindRegion(target, r);
32565         }
32566         return this.regions[target];
32567     },
32568
32569     // private (kinda)
32570     bindRegion : function(name, r){
32571         this.regions[name] = r;
32572         r.on("visibilitychange", this.layout, this);
32573         r.on("paneladded", this.layout, this);
32574         r.on("panelremoved", this.layout, this);
32575         r.on("invalidated", this.layout, this);
32576         r.on("resized", this.onRegionResized, this);
32577         r.on("collapsed", this.onRegionCollapsed, this);
32578         r.on("expanded", this.onRegionExpanded, this);
32579     },
32580
32581     /**
32582      * Performs a layout update.
32583      */
32584     layout : function(){
32585         if(this.updating) return;
32586         var size = this.getViewSize();
32587         var w = size.width;
32588         var h = size.height;
32589         var centerW = w;
32590         var centerH = h;
32591         var centerY = 0;
32592         var centerX = 0;
32593         //var x = 0, y = 0;
32594
32595         var rs = this.regions;
32596         var north = rs["north"];
32597         var south = rs["south"]; 
32598         var west = rs["west"];
32599         var east = rs["east"];
32600         var center = rs["center"];
32601         //if(this.hideOnLayout){ // not supported anymore
32602             //c.el.setStyle("display", "none");
32603         //}
32604         if(north && north.isVisible()){
32605             var b = north.getBox();
32606             var m = north.getMargins();
32607             b.width = w - (m.left+m.right);
32608             b.x = m.left;
32609             b.y = m.top;
32610             centerY = b.height + b.y + m.bottom;
32611             centerH -= centerY;
32612             north.updateBox(this.safeBox(b));
32613         }
32614         if(south && south.isVisible()){
32615             var b = south.getBox();
32616             var m = south.getMargins();
32617             b.width = w - (m.left+m.right);
32618             b.x = m.left;
32619             var totalHeight = (b.height + m.top + m.bottom);
32620             b.y = h - totalHeight + m.top;
32621             centerH -= totalHeight;
32622             south.updateBox(this.safeBox(b));
32623         }
32624         if(west && west.isVisible()){
32625             var b = west.getBox();
32626             var m = west.getMargins();
32627             b.height = centerH - (m.top+m.bottom);
32628             b.x = m.left;
32629             b.y = centerY + m.top;
32630             var totalWidth = (b.width + m.left + m.right);
32631             centerX += totalWidth;
32632             centerW -= totalWidth;
32633             west.updateBox(this.safeBox(b));
32634         }
32635         if(east && east.isVisible()){
32636             var b = east.getBox();
32637             var m = east.getMargins();
32638             b.height = centerH - (m.top+m.bottom);
32639             var totalWidth = (b.width + m.left + m.right);
32640             b.x = w - totalWidth + m.left;
32641             b.y = centerY + m.top;
32642             centerW -= totalWidth;
32643             east.updateBox(this.safeBox(b));
32644         }
32645         if(center){
32646             var m = center.getMargins();
32647             var centerBox = {
32648                 x: centerX + m.left,
32649                 y: centerY + m.top,
32650                 width: centerW - (m.left+m.right),
32651                 height: centerH - (m.top+m.bottom)
32652             };
32653             //if(this.hideOnLayout){
32654                 //center.el.setStyle("display", "block");
32655             //}
32656             center.updateBox(this.safeBox(centerBox));
32657         }
32658         this.el.repaint();
32659         this.fireEvent("layout", this);
32660     },
32661
32662     // private
32663     safeBox : function(box){
32664         box.width = Math.max(0, box.width);
32665         box.height = Math.max(0, box.height);
32666         return box;
32667     },
32668
32669     /**
32670      * Adds a ContentPanel (or subclass) to this layout.
32671      * @param {String} target The target region key (north, south, east, west or center).
32672      * @param {Roo.ContentPanel} panel The panel to add
32673      * @return {Roo.ContentPanel} The added panel
32674      */
32675     add : function(target, panel){
32676          
32677         target = target.toLowerCase();
32678         return this.regions[target].add(panel);
32679     },
32680
32681     /**
32682      * Remove a ContentPanel (or subclass) to this layout.
32683      * @param {String} target The target region key (north, south, east, west or center).
32684      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32685      * @return {Roo.ContentPanel} The removed panel
32686      */
32687     remove : function(target, panel){
32688         target = target.toLowerCase();
32689         return this.regions[target].remove(panel);
32690     },
32691
32692     /**
32693      * Searches all regions for a panel with the specified id
32694      * @param {String} panelId
32695      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32696      */
32697     findPanel : function(panelId){
32698         var rs = this.regions;
32699         for(var target in rs){
32700             if(typeof rs[target] != "function"){
32701                 var p = rs[target].getPanel(panelId);
32702                 if(p){
32703                     return p;
32704                 }
32705             }
32706         }
32707         return null;
32708     },
32709
32710     /**
32711      * Searches all regions for a panel with the specified id and activates (shows) it.
32712      * @param {String/ContentPanel} panelId The panels id or the panel itself
32713      * @return {Roo.ContentPanel} The shown panel or null
32714      */
32715     showPanel : function(panelId) {
32716       var rs = this.regions;
32717       for(var target in rs){
32718          var r = rs[target];
32719          if(typeof r != "function"){
32720             if(r.hasPanel(panelId)){
32721                return r.showPanel(panelId);
32722             }
32723          }
32724       }
32725       return null;
32726    },
32727
32728    /**
32729      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32730      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32731      */
32732     restoreState : function(provider){
32733         if(!provider){
32734             provider = Roo.state.Manager;
32735         }
32736         var sm = new Roo.LayoutStateManager();
32737         sm.init(this, provider);
32738     },
32739
32740     /**
32741      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32742      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32743      * a valid ContentPanel config object.  Example:
32744      * <pre><code>
32745 // Create the main layout
32746 var layout = new Roo.BorderLayout('main-ct', {
32747     west: {
32748         split:true,
32749         minSize: 175,
32750         titlebar: true
32751     },
32752     center: {
32753         title:'Components'
32754     }
32755 }, 'main-ct');
32756
32757 // Create and add multiple ContentPanels at once via configs
32758 layout.batchAdd({
32759    west: {
32760        id: 'source-files',
32761        autoCreate:true,
32762        title:'Ext Source Files',
32763        autoScroll:true,
32764        fitToFrame:true
32765    },
32766    center : {
32767        el: cview,
32768        autoScroll:true,
32769        fitToFrame:true,
32770        toolbar: tb,
32771        resizeEl:'cbody'
32772    }
32773 });
32774 </code></pre>
32775      * @param {Object} regions An object containing ContentPanel configs by region name
32776      */
32777     batchAdd : function(regions){
32778         this.beginUpdate();
32779         for(var rname in regions){
32780             var lr = this.regions[rname];
32781             if(lr){
32782                 this.addTypedPanels(lr, regions[rname]);
32783             }
32784         }
32785         this.endUpdate();
32786     },
32787
32788     // private
32789     addTypedPanels : function(lr, ps){
32790         if(typeof ps == 'string'){
32791             lr.add(new Roo.ContentPanel(ps));
32792         }
32793         else if(ps instanceof Array){
32794             for(var i =0, len = ps.length; i < len; i++){
32795                 this.addTypedPanels(lr, ps[i]);
32796             }
32797         }
32798         else if(!ps.events){ // raw config?
32799             var el = ps.el;
32800             delete ps.el; // prevent conflict
32801             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32802         }
32803         else {  // panel object assumed!
32804             lr.add(ps);
32805         }
32806     },
32807     /**
32808      * Adds a xtype elements to the layout.
32809      * <pre><code>
32810
32811 layout.addxtype({
32812        xtype : 'ContentPanel',
32813        region: 'west',
32814        items: [ .... ]
32815    }
32816 );
32817
32818 layout.addxtype({
32819         xtype : 'NestedLayoutPanel',
32820         region: 'west',
32821         layout: {
32822            center: { },
32823            west: { }   
32824         },
32825         items : [ ... list of content panels or nested layout panels.. ]
32826    }
32827 );
32828 </code></pre>
32829      * @param {Object} cfg Xtype definition of item to add.
32830      */
32831     addxtype : function(cfg)
32832     {
32833         // basically accepts a pannel...
32834         // can accept a layout region..!?!?
32835         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32836         
32837         if (!cfg.xtype.match(/Panel$/)) {
32838             return false;
32839         }
32840         var ret = false;
32841         
32842         if (typeof(cfg.region) == 'undefined') {
32843             Roo.log("Failed to add Panel, region was not set");
32844             Roo.log(cfg);
32845             return false;
32846         }
32847         var region = cfg.region;
32848         delete cfg.region;
32849         
32850           
32851         var xitems = [];
32852         if (cfg.items) {
32853             xitems = cfg.items;
32854             delete cfg.items;
32855         }
32856         var nb = false;
32857         
32858         switch(cfg.xtype) 
32859         {
32860             case 'ContentPanel':  // ContentPanel (el, cfg)
32861             case 'ScrollPanel':  // ContentPanel (el, cfg)
32862             case 'ViewPanel': 
32863                 if(cfg.autoCreate) {
32864                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32865                 } else {
32866                     var el = this.el.createChild();
32867                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32868                 }
32869                 
32870                 this.add(region, ret);
32871                 break;
32872             
32873             
32874             case 'TreePanel': // our new panel!
32875                 cfg.el = this.el.createChild();
32876                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32877                 this.add(region, ret);
32878                 break;
32879             
32880             case 'NestedLayoutPanel': 
32881                 // create a new Layout (which is  a Border Layout...
32882                 var el = this.el.createChild();
32883                 var clayout = cfg.layout;
32884                 delete cfg.layout;
32885                 clayout.items   = clayout.items  || [];
32886                 // replace this exitems with the clayout ones..
32887                 xitems = clayout.items;
32888                  
32889                 
32890                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32891                     cfg.background = false;
32892                 }
32893                 var layout = new Roo.BorderLayout(el, clayout);
32894                 
32895                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32896                 //console.log('adding nested layout panel '  + cfg.toSource());
32897                 this.add(region, ret);
32898                 nb = {}; /// find first...
32899                 break;
32900                 
32901             case 'GridPanel': 
32902             
32903                 // needs grid and region
32904                 
32905                 //var el = this.getRegion(region).el.createChild();
32906                 var el = this.el.createChild();
32907                 // create the grid first...
32908                 
32909                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32910                 delete cfg.grid;
32911                 if (region == 'center' && this.active ) {
32912                     cfg.background = false;
32913                 }
32914                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32915                 
32916                 this.add(region, ret);
32917                 if (cfg.background) {
32918                     ret.on('activate', function(gp) {
32919                         if (!gp.grid.rendered) {
32920                             gp.grid.render();
32921                         }
32922                     });
32923                 } else {
32924                     grid.render();
32925                 }
32926                 break;
32927            
32928            
32929            
32930                 
32931                 
32932                 
32933             default:
32934                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32935                     
32936                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32937                     this.add(region, ret);
32938                 } else {
32939                 
32940                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32941                     return null;
32942                 }
32943                 
32944              // GridPanel (grid, cfg)
32945             
32946         }
32947         this.beginUpdate();
32948         // add children..
32949         var region = '';
32950         var abn = {};
32951         Roo.each(xitems, function(i)  {
32952             region = nb && i.region ? i.region : false;
32953             
32954             var add = ret.addxtype(i);
32955            
32956             if (region) {
32957                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32958                 if (!i.background) {
32959                     abn[region] = nb[region] ;
32960                 }
32961             }
32962             
32963         });
32964         this.endUpdate();
32965
32966         // make the last non-background panel active..
32967         //if (nb) { Roo.log(abn); }
32968         if (nb) {
32969             
32970             for(var r in abn) {
32971                 region = this.getRegion(r);
32972                 if (region) {
32973                     // tried using nb[r], but it does not work..
32974                      
32975                     region.showPanel(abn[r]);
32976                    
32977                 }
32978             }
32979         }
32980         return ret;
32981         
32982     }
32983 });
32984
32985 /**
32986  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32987  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32988  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32989  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32990  * <pre><code>
32991 // shorthand
32992 var CP = Roo.ContentPanel;
32993
32994 var layout = Roo.BorderLayout.create({
32995     north: {
32996         initialSize: 25,
32997         titlebar: false,
32998         panels: [new CP("north", "North")]
32999     },
33000     west: {
33001         split:true,
33002         initialSize: 200,
33003         minSize: 175,
33004         maxSize: 400,
33005         titlebar: true,
33006         collapsible: true,
33007         panels: [new CP("west", {title: "West"})]
33008     },
33009     east: {
33010         split:true,
33011         initialSize: 202,
33012         minSize: 175,
33013         maxSize: 400,
33014         titlebar: true,
33015         collapsible: true,
33016         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33017     },
33018     south: {
33019         split:true,
33020         initialSize: 100,
33021         minSize: 100,
33022         maxSize: 200,
33023         titlebar: true,
33024         collapsible: true,
33025         panels: [new CP("south", {title: "South", closable: true})]
33026     },
33027     center: {
33028         titlebar: true,
33029         autoScroll:true,
33030         resizeTabs: true,
33031         minTabWidth: 50,
33032         preferredTabWidth: 150,
33033         panels: [
33034             new CP("center1", {title: "Close Me", closable: true}),
33035             new CP("center2", {title: "Center Panel", closable: false})
33036         ]
33037     }
33038 }, document.body);
33039
33040 layout.getRegion("center").showPanel("center1");
33041 </code></pre>
33042  * @param config
33043  * @param targetEl
33044  */
33045 Roo.BorderLayout.create = function(config, targetEl){
33046     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33047     layout.beginUpdate();
33048     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33049     for(var j = 0, jlen = regions.length; j < jlen; j++){
33050         var lr = regions[j];
33051         if(layout.regions[lr] && config[lr].panels){
33052             var r = layout.regions[lr];
33053             var ps = config[lr].panels;
33054             layout.addTypedPanels(r, ps);
33055         }
33056     }
33057     layout.endUpdate();
33058     return layout;
33059 };
33060
33061 // private
33062 Roo.BorderLayout.RegionFactory = {
33063     // private
33064     validRegions : ["north","south","east","west","center"],
33065
33066     // private
33067     create : function(target, mgr, config){
33068         target = target.toLowerCase();
33069         if(config.lightweight || config.basic){
33070             return new Roo.BasicLayoutRegion(mgr, config, target);
33071         }
33072         switch(target){
33073             case "north":
33074                 return new Roo.NorthLayoutRegion(mgr, config);
33075             case "south":
33076                 return new Roo.SouthLayoutRegion(mgr, config);
33077             case "east":
33078                 return new Roo.EastLayoutRegion(mgr, config);
33079             case "west":
33080                 return new Roo.WestLayoutRegion(mgr, config);
33081             case "center":
33082                 return new Roo.CenterLayoutRegion(mgr, config);
33083         }
33084         throw 'Layout region "'+target+'" not supported.';
33085     }
33086 };/*
33087  * Based on:
33088  * Ext JS Library 1.1.1
33089  * Copyright(c) 2006-2007, Ext JS, LLC.
33090  *
33091  * Originally Released Under LGPL - original licence link has changed is not relivant.
33092  *
33093  * Fork - LGPL
33094  * <script type="text/javascript">
33095  */
33096  
33097 /**
33098  * @class Roo.BasicLayoutRegion
33099  * @extends Roo.util.Observable
33100  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33101  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33102  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33103  */
33104 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33105     this.mgr = mgr;
33106     this.position  = pos;
33107     this.events = {
33108         /**
33109          * @scope Roo.BasicLayoutRegion
33110          */
33111         
33112         /**
33113          * @event beforeremove
33114          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33115          * @param {Roo.LayoutRegion} this
33116          * @param {Roo.ContentPanel} panel The panel
33117          * @param {Object} e The cancel event object
33118          */
33119         "beforeremove" : true,
33120         /**
33121          * @event invalidated
33122          * Fires when the layout for this region is changed.
33123          * @param {Roo.LayoutRegion} this
33124          */
33125         "invalidated" : true,
33126         /**
33127          * @event visibilitychange
33128          * Fires when this region is shown or hidden 
33129          * @param {Roo.LayoutRegion} this
33130          * @param {Boolean} visibility true or false
33131          */
33132         "visibilitychange" : true,
33133         /**
33134          * @event paneladded
33135          * Fires when a panel is added. 
33136          * @param {Roo.LayoutRegion} this
33137          * @param {Roo.ContentPanel} panel The panel
33138          */
33139         "paneladded" : true,
33140         /**
33141          * @event panelremoved
33142          * Fires when a panel is removed. 
33143          * @param {Roo.LayoutRegion} this
33144          * @param {Roo.ContentPanel} panel The panel
33145          */
33146         "panelremoved" : true,
33147         /**
33148          * @event collapsed
33149          * Fires when this region is collapsed.
33150          * @param {Roo.LayoutRegion} this
33151          */
33152         "collapsed" : true,
33153         /**
33154          * @event expanded
33155          * Fires when this region is expanded.
33156          * @param {Roo.LayoutRegion} this
33157          */
33158         "expanded" : true,
33159         /**
33160          * @event slideshow
33161          * Fires when this region is slid into view.
33162          * @param {Roo.LayoutRegion} this
33163          */
33164         "slideshow" : true,
33165         /**
33166          * @event slidehide
33167          * Fires when this region slides out of view. 
33168          * @param {Roo.LayoutRegion} this
33169          */
33170         "slidehide" : true,
33171         /**
33172          * @event panelactivated
33173          * Fires when a panel is activated. 
33174          * @param {Roo.LayoutRegion} this
33175          * @param {Roo.ContentPanel} panel The activated panel
33176          */
33177         "panelactivated" : true,
33178         /**
33179          * @event resized
33180          * Fires when the user resizes this region. 
33181          * @param {Roo.LayoutRegion} this
33182          * @param {Number} newSize The new size (width for east/west, height for north/south)
33183          */
33184         "resized" : true
33185     };
33186     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33187     this.panels = new Roo.util.MixedCollection();
33188     this.panels.getKey = this.getPanelId.createDelegate(this);
33189     this.box = null;
33190     this.activePanel = null;
33191     // ensure listeners are added...
33192     
33193     if (config.listeners || config.events) {
33194         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33195             listeners : config.listeners || {},
33196             events : config.events || {}
33197         });
33198     }
33199     
33200     if(skipConfig !== true){
33201         this.applyConfig(config);
33202     }
33203 };
33204
33205 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33206     getPanelId : function(p){
33207         return p.getId();
33208     },
33209     
33210     applyConfig : function(config){
33211         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33212         this.config = config;
33213         
33214     },
33215     
33216     /**
33217      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33218      * the width, for horizontal (north, south) the height.
33219      * @param {Number} newSize The new width or height
33220      */
33221     resizeTo : function(newSize){
33222         var el = this.el ? this.el :
33223                  (this.activePanel ? this.activePanel.getEl() : null);
33224         if(el){
33225             switch(this.position){
33226                 case "east":
33227                 case "west":
33228                     el.setWidth(newSize);
33229                     this.fireEvent("resized", this, newSize);
33230                 break;
33231                 case "north":
33232                 case "south":
33233                     el.setHeight(newSize);
33234                     this.fireEvent("resized", this, newSize);
33235                 break;                
33236             }
33237         }
33238     },
33239     
33240     getBox : function(){
33241         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33242     },
33243     
33244     getMargins : function(){
33245         return this.margins;
33246     },
33247     
33248     updateBox : function(box){
33249         this.box = box;
33250         var el = this.activePanel.getEl();
33251         el.dom.style.left = box.x + "px";
33252         el.dom.style.top = box.y + "px";
33253         this.activePanel.setSize(box.width, box.height);
33254     },
33255     
33256     /**
33257      * Returns the container element for this region.
33258      * @return {Roo.Element}
33259      */
33260     getEl : function(){
33261         return this.activePanel;
33262     },
33263     
33264     /**
33265      * Returns true if this region is currently visible.
33266      * @return {Boolean}
33267      */
33268     isVisible : function(){
33269         return this.activePanel ? true : false;
33270     },
33271     
33272     setActivePanel : function(panel){
33273         panel = this.getPanel(panel);
33274         if(this.activePanel && this.activePanel != panel){
33275             this.activePanel.setActiveState(false);
33276             this.activePanel.getEl().setLeftTop(-10000,-10000);
33277         }
33278         this.activePanel = panel;
33279         panel.setActiveState(true);
33280         if(this.box){
33281             panel.setSize(this.box.width, this.box.height);
33282         }
33283         this.fireEvent("panelactivated", this, panel);
33284         this.fireEvent("invalidated");
33285     },
33286     
33287     /**
33288      * Show the specified panel.
33289      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33290      * @return {Roo.ContentPanel} The shown panel or null
33291      */
33292     showPanel : function(panel){
33293         if(panel = this.getPanel(panel)){
33294             this.setActivePanel(panel);
33295         }
33296         return panel;
33297     },
33298     
33299     /**
33300      * Get the active panel for this region.
33301      * @return {Roo.ContentPanel} The active panel or null
33302      */
33303     getActivePanel : function(){
33304         return this.activePanel;
33305     },
33306     
33307     /**
33308      * Add the passed ContentPanel(s)
33309      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33310      * @return {Roo.ContentPanel} The panel added (if only one was added)
33311      */
33312     add : function(panel){
33313         if(arguments.length > 1){
33314             for(var i = 0, len = arguments.length; i < len; i++) {
33315                 this.add(arguments[i]);
33316             }
33317             return null;
33318         }
33319         if(this.hasPanel(panel)){
33320             this.showPanel(panel);
33321             return panel;
33322         }
33323         var el = panel.getEl();
33324         if(el.dom.parentNode != this.mgr.el.dom){
33325             this.mgr.el.dom.appendChild(el.dom);
33326         }
33327         if(panel.setRegion){
33328             panel.setRegion(this);
33329         }
33330         this.panels.add(panel);
33331         el.setStyle("position", "absolute");
33332         if(!panel.background){
33333             this.setActivePanel(panel);
33334             if(this.config.initialSize && this.panels.getCount()==1){
33335                 this.resizeTo(this.config.initialSize);
33336             }
33337         }
33338         this.fireEvent("paneladded", this, panel);
33339         return panel;
33340     },
33341     
33342     /**
33343      * Returns true if the panel is in this region.
33344      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33345      * @return {Boolean}
33346      */
33347     hasPanel : function(panel){
33348         if(typeof panel == "object"){ // must be panel obj
33349             panel = panel.getId();
33350         }
33351         return this.getPanel(panel) ? true : false;
33352     },
33353     
33354     /**
33355      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33356      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33357      * @param {Boolean} preservePanel Overrides the config preservePanel option
33358      * @return {Roo.ContentPanel} The panel that was removed
33359      */
33360     remove : function(panel, preservePanel){
33361         panel = this.getPanel(panel);
33362         if(!panel){
33363             return null;
33364         }
33365         var e = {};
33366         this.fireEvent("beforeremove", this, panel, e);
33367         if(e.cancel === true){
33368             return null;
33369         }
33370         var panelId = panel.getId();
33371         this.panels.removeKey(panelId);
33372         return panel;
33373     },
33374     
33375     /**
33376      * Returns the panel specified or null if it's not in this region.
33377      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33378      * @return {Roo.ContentPanel}
33379      */
33380     getPanel : function(id){
33381         if(typeof id == "object"){ // must be panel obj
33382             return id;
33383         }
33384         return this.panels.get(id);
33385     },
33386     
33387     /**
33388      * Returns this regions position (north/south/east/west/center).
33389      * @return {String} 
33390      */
33391     getPosition: function(){
33392         return this.position;    
33393     }
33394 });/*
33395  * Based on:
33396  * Ext JS Library 1.1.1
33397  * Copyright(c) 2006-2007, Ext JS, LLC.
33398  *
33399  * Originally Released Under LGPL - original licence link has changed is not relivant.
33400  *
33401  * Fork - LGPL
33402  * <script type="text/javascript">
33403  */
33404  
33405 /**
33406  * @class Roo.LayoutRegion
33407  * @extends Roo.BasicLayoutRegion
33408  * This class represents a region in a layout manager.
33409  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33410  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33411  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33412  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33413  * @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})
33414  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33415  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33416  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33417  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33418  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33419  * @cfg {String}    title           The title for the region (overrides panel titles)
33420  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33421  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33422  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33423  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33424  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33425  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33426  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33427  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33428  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33429  * @cfg {Boolean}   showPin         True to show a pin button
33430  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33431  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33432  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33433  * @cfg {Number}    width           For East/West panels
33434  * @cfg {Number}    height          For North/South panels
33435  * @cfg {Boolean}   split           To show the splitter
33436  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33437  */
33438 Roo.LayoutRegion = function(mgr, config, pos){
33439     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33440     var dh = Roo.DomHelper;
33441     /** This region's container element 
33442     * @type Roo.Element */
33443     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33444     /** This region's title element 
33445     * @type Roo.Element */
33446
33447     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33448         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33449         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33450     ]}, true);
33451     this.titleEl.enableDisplayMode();
33452     /** This region's title text element 
33453     * @type HTMLElement */
33454     this.titleTextEl = this.titleEl.dom.firstChild;
33455     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33456     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33457     this.closeBtn.enableDisplayMode();
33458     this.closeBtn.on("click", this.closeClicked, this);
33459     this.closeBtn.hide();
33460
33461     this.createBody(config);
33462     this.visible = true;
33463     this.collapsed = false;
33464
33465     if(config.hideWhenEmpty){
33466         this.hide();
33467         this.on("paneladded", this.validateVisibility, this);
33468         this.on("panelremoved", this.validateVisibility, this);
33469     }
33470     this.applyConfig(config);
33471 };
33472
33473 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33474
33475     createBody : function(){
33476         /** This region's body element 
33477         * @type Roo.Element */
33478         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33479     },
33480
33481     applyConfig : function(c){
33482         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33483             var dh = Roo.DomHelper;
33484             if(c.titlebar !== false){
33485                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33486                 this.collapseBtn.on("click", this.collapse, this);
33487                 this.collapseBtn.enableDisplayMode();
33488
33489                 if(c.showPin === true || this.showPin){
33490                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33491                     this.stickBtn.enableDisplayMode();
33492                     this.stickBtn.on("click", this.expand, this);
33493                     this.stickBtn.hide();
33494                 }
33495             }
33496             /** This region's collapsed element
33497             * @type Roo.Element */
33498             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33499                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33500             ]}, true);
33501             if(c.floatable !== false){
33502                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33503                this.collapsedEl.on("click", this.collapseClick, this);
33504             }
33505
33506             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33507                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33508                    id: "message", unselectable: "on", style:{"float":"left"}});
33509                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33510              }
33511             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33512             this.expandBtn.on("click", this.expand, this);
33513         }
33514         if(this.collapseBtn){
33515             this.collapseBtn.setVisible(c.collapsible == true);
33516         }
33517         this.cmargins = c.cmargins || this.cmargins ||
33518                          (this.position == "west" || this.position == "east" ?
33519                              {top: 0, left: 2, right:2, bottom: 0} :
33520                              {top: 2, left: 0, right:0, bottom: 2});
33521         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33522         this.bottomTabs = c.tabPosition != "top";
33523         this.autoScroll = c.autoScroll || false;
33524         if(this.autoScroll){
33525             this.bodyEl.setStyle("overflow", "auto");
33526         }else{
33527             this.bodyEl.setStyle("overflow", "hidden");
33528         }
33529         //if(c.titlebar !== false){
33530             if((!c.titlebar && !c.title) || c.titlebar === false){
33531                 this.titleEl.hide();
33532             }else{
33533                 this.titleEl.show();
33534                 if(c.title){
33535                     this.titleTextEl.innerHTML = c.title;
33536                 }
33537             }
33538         //}
33539         this.duration = c.duration || .30;
33540         this.slideDuration = c.slideDuration || .45;
33541         this.config = c;
33542         if(c.collapsed){
33543             this.collapse(true);
33544         }
33545         if(c.hidden){
33546             this.hide();
33547         }
33548     },
33549     /**
33550      * Returns true if this region is currently visible.
33551      * @return {Boolean}
33552      */
33553     isVisible : function(){
33554         return this.visible;
33555     },
33556
33557     /**
33558      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33559      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33560      */
33561     setCollapsedTitle : function(title){
33562         title = title || "&#160;";
33563         if(this.collapsedTitleTextEl){
33564             this.collapsedTitleTextEl.innerHTML = title;
33565         }
33566     },
33567
33568     getBox : function(){
33569         var b;
33570         if(!this.collapsed){
33571             b = this.el.getBox(false, true);
33572         }else{
33573             b = this.collapsedEl.getBox(false, true);
33574         }
33575         return b;
33576     },
33577
33578     getMargins : function(){
33579         return this.collapsed ? this.cmargins : this.margins;
33580     },
33581
33582     highlight : function(){
33583         this.el.addClass("x-layout-panel-dragover");
33584     },
33585
33586     unhighlight : function(){
33587         this.el.removeClass("x-layout-panel-dragover");
33588     },
33589
33590     updateBox : function(box){
33591         this.box = box;
33592         if(!this.collapsed){
33593             this.el.dom.style.left = box.x + "px";
33594             this.el.dom.style.top = box.y + "px";
33595             this.updateBody(box.width, box.height);
33596         }else{
33597             this.collapsedEl.dom.style.left = box.x + "px";
33598             this.collapsedEl.dom.style.top = box.y + "px";
33599             this.collapsedEl.setSize(box.width, box.height);
33600         }
33601         if(this.tabs){
33602             this.tabs.autoSizeTabs();
33603         }
33604     },
33605
33606     updateBody : function(w, h){
33607         if(w !== null){
33608             this.el.setWidth(w);
33609             w -= this.el.getBorderWidth("rl");
33610             if(this.config.adjustments){
33611                 w += this.config.adjustments[0];
33612             }
33613         }
33614         if(h !== null){
33615             this.el.setHeight(h);
33616             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33617             h -= this.el.getBorderWidth("tb");
33618             if(this.config.adjustments){
33619                 h += this.config.adjustments[1];
33620             }
33621             this.bodyEl.setHeight(h);
33622             if(this.tabs){
33623                 h = this.tabs.syncHeight(h);
33624             }
33625         }
33626         if(this.panelSize){
33627             w = w !== null ? w : this.panelSize.width;
33628             h = h !== null ? h : this.panelSize.height;
33629         }
33630         if(this.activePanel){
33631             var el = this.activePanel.getEl();
33632             w = w !== null ? w : el.getWidth();
33633             h = h !== null ? h : el.getHeight();
33634             this.panelSize = {width: w, height: h};
33635             this.activePanel.setSize(w, h);
33636         }
33637         if(Roo.isIE && this.tabs){
33638             this.tabs.el.repaint();
33639         }
33640     },
33641
33642     /**
33643      * Returns the container element for this region.
33644      * @return {Roo.Element}
33645      */
33646     getEl : function(){
33647         return this.el;
33648     },
33649
33650     /**
33651      * Hides this region.
33652      */
33653     hide : function(){
33654         if(!this.collapsed){
33655             this.el.dom.style.left = "-2000px";
33656             this.el.hide();
33657         }else{
33658             this.collapsedEl.dom.style.left = "-2000px";
33659             this.collapsedEl.hide();
33660         }
33661         this.visible = false;
33662         this.fireEvent("visibilitychange", this, false);
33663     },
33664
33665     /**
33666      * Shows this region if it was previously hidden.
33667      */
33668     show : function(){
33669         if(!this.collapsed){
33670             this.el.show();
33671         }else{
33672             this.collapsedEl.show();
33673         }
33674         this.visible = true;
33675         this.fireEvent("visibilitychange", this, true);
33676     },
33677
33678     closeClicked : function(){
33679         if(this.activePanel){
33680             this.remove(this.activePanel);
33681         }
33682     },
33683
33684     collapseClick : function(e){
33685         if(this.isSlid){
33686            e.stopPropagation();
33687            this.slideIn();
33688         }else{
33689            e.stopPropagation();
33690            this.slideOut();
33691         }
33692     },
33693
33694     /**
33695      * Collapses this region.
33696      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33697      */
33698     collapse : function(skipAnim){
33699         if(this.collapsed) return;
33700         this.collapsed = true;
33701         if(this.split){
33702             this.split.el.hide();
33703         }
33704         if(this.config.animate && skipAnim !== true){
33705             this.fireEvent("invalidated", this);
33706             this.animateCollapse();
33707         }else{
33708             this.el.setLocation(-20000,-20000);
33709             this.el.hide();
33710             this.collapsedEl.show();
33711             this.fireEvent("collapsed", this);
33712             this.fireEvent("invalidated", this);
33713         }
33714     },
33715
33716     animateCollapse : function(){
33717         // overridden
33718     },
33719
33720     /**
33721      * Expands this region if it was previously collapsed.
33722      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33723      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33724      */
33725     expand : function(e, skipAnim){
33726         if(e) e.stopPropagation();
33727         if(!this.collapsed || this.el.hasActiveFx()) return;
33728         if(this.isSlid){
33729             this.afterSlideIn();
33730             skipAnim = true;
33731         }
33732         this.collapsed = false;
33733         if(this.config.animate && skipAnim !== true){
33734             this.animateExpand();
33735         }else{
33736             this.el.show();
33737             if(this.split){
33738                 this.split.el.show();
33739             }
33740             this.collapsedEl.setLocation(-2000,-2000);
33741             this.collapsedEl.hide();
33742             this.fireEvent("invalidated", this);
33743             this.fireEvent("expanded", this);
33744         }
33745     },
33746
33747     animateExpand : function(){
33748         // overridden
33749     },
33750
33751     initTabs : function()
33752     {
33753         this.bodyEl.setStyle("overflow", "hidden");
33754         var ts = new Roo.TabPanel(
33755                 this.bodyEl.dom,
33756                 {
33757                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33758                     disableTooltips: this.config.disableTabTips,
33759                     toolbar : this.config.toolbar
33760                 }
33761         );
33762         if(this.config.hideTabs){
33763             ts.stripWrap.setDisplayed(false);
33764         }
33765         this.tabs = ts;
33766         ts.resizeTabs = this.config.resizeTabs === true;
33767         ts.minTabWidth = this.config.minTabWidth || 40;
33768         ts.maxTabWidth = this.config.maxTabWidth || 250;
33769         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33770         ts.monitorResize = false;
33771         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33772         ts.bodyEl.addClass('x-layout-tabs-body');
33773         this.panels.each(this.initPanelAsTab, this);
33774     },
33775
33776     initPanelAsTab : function(panel){
33777         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33778                     this.config.closeOnTab && panel.isClosable());
33779         if(panel.tabTip !== undefined){
33780             ti.setTooltip(panel.tabTip);
33781         }
33782         ti.on("activate", function(){
33783               this.setActivePanel(panel);
33784         }, this);
33785         if(this.config.closeOnTab){
33786             ti.on("beforeclose", function(t, e){
33787                 e.cancel = true;
33788                 this.remove(panel);
33789             }, this);
33790         }
33791         return ti;
33792     },
33793
33794     updatePanelTitle : function(panel, title){
33795         if(this.activePanel == panel){
33796             this.updateTitle(title);
33797         }
33798         if(this.tabs){
33799             var ti = this.tabs.getTab(panel.getEl().id);
33800             ti.setText(title);
33801             if(panel.tabTip !== undefined){
33802                 ti.setTooltip(panel.tabTip);
33803             }
33804         }
33805     },
33806
33807     updateTitle : function(title){
33808         if(this.titleTextEl && !this.config.title){
33809             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33810         }
33811     },
33812
33813     setActivePanel : function(panel){
33814         panel = this.getPanel(panel);
33815         if(this.activePanel && this.activePanel != panel){
33816             this.activePanel.setActiveState(false);
33817         }
33818         this.activePanel = panel;
33819         panel.setActiveState(true);
33820         if(this.panelSize){
33821             panel.setSize(this.panelSize.width, this.panelSize.height);
33822         }
33823         if(this.closeBtn){
33824             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33825         }
33826         this.updateTitle(panel.getTitle());
33827         if(this.tabs){
33828             this.fireEvent("invalidated", this);
33829         }
33830         this.fireEvent("panelactivated", this, panel);
33831     },
33832
33833     /**
33834      * Shows the specified panel.
33835      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33836      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33837      */
33838     showPanel : function(panel){
33839         if(panel = this.getPanel(panel)){
33840             if(this.tabs){
33841                 var tab = this.tabs.getTab(panel.getEl().id);
33842                 if(tab.isHidden()){
33843                     this.tabs.unhideTab(tab.id);
33844                 }
33845                 tab.activate();
33846             }else{
33847                 this.setActivePanel(panel);
33848             }
33849         }
33850         return panel;
33851     },
33852
33853     /**
33854      * Get the active panel for this region.
33855      * @return {Roo.ContentPanel} The active panel or null
33856      */
33857     getActivePanel : function(){
33858         return this.activePanel;
33859     },
33860
33861     validateVisibility : function(){
33862         if(this.panels.getCount() < 1){
33863             this.updateTitle("&#160;");
33864             this.closeBtn.hide();
33865             this.hide();
33866         }else{
33867             if(!this.isVisible()){
33868                 this.show();
33869             }
33870         }
33871     },
33872
33873     /**
33874      * Adds the passed ContentPanel(s) to this region.
33875      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33876      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33877      */
33878     add : function(panel){
33879         if(arguments.length > 1){
33880             for(var i = 0, len = arguments.length; i < len; i++) {
33881                 this.add(arguments[i]);
33882             }
33883             return null;
33884         }
33885         if(this.hasPanel(panel)){
33886             this.showPanel(panel);
33887             return panel;
33888         }
33889         panel.setRegion(this);
33890         this.panels.add(panel);
33891         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33892             this.bodyEl.dom.appendChild(panel.getEl().dom);
33893             if(panel.background !== true){
33894                 this.setActivePanel(panel);
33895             }
33896             this.fireEvent("paneladded", this, panel);
33897             return panel;
33898         }
33899         if(!this.tabs){
33900             this.initTabs();
33901         }else{
33902             this.initPanelAsTab(panel);
33903         }
33904         if(panel.background !== true){
33905             this.tabs.activate(panel.getEl().id);
33906         }
33907         this.fireEvent("paneladded", this, panel);
33908         return panel;
33909     },
33910
33911     /**
33912      * Hides the tab for the specified panel.
33913      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33914      */
33915     hidePanel : function(panel){
33916         if(this.tabs && (panel = this.getPanel(panel))){
33917             this.tabs.hideTab(panel.getEl().id);
33918         }
33919     },
33920
33921     /**
33922      * Unhides the tab for a previously hidden panel.
33923      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33924      */
33925     unhidePanel : function(panel){
33926         if(this.tabs && (panel = this.getPanel(panel))){
33927             this.tabs.unhideTab(panel.getEl().id);
33928         }
33929     },
33930
33931     clearPanels : function(){
33932         while(this.panels.getCount() > 0){
33933              this.remove(this.panels.first());
33934         }
33935     },
33936
33937     /**
33938      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33939      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33940      * @param {Boolean} preservePanel Overrides the config preservePanel option
33941      * @return {Roo.ContentPanel} The panel that was removed
33942      */
33943     remove : function(panel, preservePanel){
33944         panel = this.getPanel(panel);
33945         if(!panel){
33946             return null;
33947         }
33948         var e = {};
33949         this.fireEvent("beforeremove", this, panel, e);
33950         if(e.cancel === true){
33951             return null;
33952         }
33953         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33954         var panelId = panel.getId();
33955         this.panels.removeKey(panelId);
33956         if(preservePanel){
33957             document.body.appendChild(panel.getEl().dom);
33958         }
33959         if(this.tabs){
33960             this.tabs.removeTab(panel.getEl().id);
33961         }else if (!preservePanel){
33962             this.bodyEl.dom.removeChild(panel.getEl().dom);
33963         }
33964         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33965             var p = this.panels.first();
33966             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33967             tempEl.appendChild(p.getEl().dom);
33968             this.bodyEl.update("");
33969             this.bodyEl.dom.appendChild(p.getEl().dom);
33970             tempEl = null;
33971             this.updateTitle(p.getTitle());
33972             this.tabs = null;
33973             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33974             this.setActivePanel(p);
33975         }
33976         panel.setRegion(null);
33977         if(this.activePanel == panel){
33978             this.activePanel = null;
33979         }
33980         if(this.config.autoDestroy !== false && preservePanel !== true){
33981             try{panel.destroy();}catch(e){}
33982         }
33983         this.fireEvent("panelremoved", this, panel);
33984         return panel;
33985     },
33986
33987     /**
33988      * Returns the TabPanel component used by this region
33989      * @return {Roo.TabPanel}
33990      */
33991     getTabs : function(){
33992         return this.tabs;
33993     },
33994
33995     createTool : function(parentEl, className){
33996         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33997             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33998         btn.addClassOnOver("x-layout-tools-button-over");
33999         return btn;
34000     }
34001 });/*
34002  * Based on:
34003  * Ext JS Library 1.1.1
34004  * Copyright(c) 2006-2007, Ext JS, LLC.
34005  *
34006  * Originally Released Under LGPL - original licence link has changed is not relivant.
34007  *
34008  * Fork - LGPL
34009  * <script type="text/javascript">
34010  */
34011  
34012
34013
34014 /**
34015  * @class Roo.SplitLayoutRegion
34016  * @extends Roo.LayoutRegion
34017  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34018  */
34019 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34020     this.cursor = cursor;
34021     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34022 };
34023
34024 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34025     splitTip : "Drag to resize.",
34026     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34027     useSplitTips : false,
34028
34029     applyConfig : function(config){
34030         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34031         if(config.split){
34032             if(!this.split){
34033                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34034                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34035                 /** The SplitBar for this region 
34036                 * @type Roo.SplitBar */
34037                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34038                 this.split.on("moved", this.onSplitMove, this);
34039                 this.split.useShim = config.useShim === true;
34040                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34041                 if(this.useSplitTips){
34042                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34043                 }
34044                 if(config.collapsible){
34045                     this.split.el.on("dblclick", this.collapse,  this);
34046                 }
34047             }
34048             if(typeof config.minSize != "undefined"){
34049                 this.split.minSize = config.minSize;
34050             }
34051             if(typeof config.maxSize != "undefined"){
34052                 this.split.maxSize = config.maxSize;
34053             }
34054             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34055                 this.hideSplitter();
34056             }
34057         }
34058     },
34059
34060     getHMaxSize : function(){
34061          var cmax = this.config.maxSize || 10000;
34062          var center = this.mgr.getRegion("center");
34063          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34064     },
34065
34066     getVMaxSize : function(){
34067          var cmax = this.config.maxSize || 10000;
34068          var center = this.mgr.getRegion("center");
34069          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34070     },
34071
34072     onSplitMove : function(split, newSize){
34073         this.fireEvent("resized", this, newSize);
34074     },
34075     
34076     /** 
34077      * Returns the {@link Roo.SplitBar} for this region.
34078      * @return {Roo.SplitBar}
34079      */
34080     getSplitBar : function(){
34081         return this.split;
34082     },
34083     
34084     hide : function(){
34085         this.hideSplitter();
34086         Roo.SplitLayoutRegion.superclass.hide.call(this);
34087     },
34088
34089     hideSplitter : function(){
34090         if(this.split){
34091             this.split.el.setLocation(-2000,-2000);
34092             this.split.el.hide();
34093         }
34094     },
34095
34096     show : function(){
34097         if(this.split){
34098             this.split.el.show();
34099         }
34100         Roo.SplitLayoutRegion.superclass.show.call(this);
34101     },
34102     
34103     beforeSlide: function(){
34104         if(Roo.isGecko){// firefox overflow auto bug workaround
34105             this.bodyEl.clip();
34106             if(this.tabs) this.tabs.bodyEl.clip();
34107             if(this.activePanel){
34108                 this.activePanel.getEl().clip();
34109                 
34110                 if(this.activePanel.beforeSlide){
34111                     this.activePanel.beforeSlide();
34112                 }
34113             }
34114         }
34115     },
34116     
34117     afterSlide : function(){
34118         if(Roo.isGecko){// firefox overflow auto bug workaround
34119             this.bodyEl.unclip();
34120             if(this.tabs) this.tabs.bodyEl.unclip();
34121             if(this.activePanel){
34122                 this.activePanel.getEl().unclip();
34123                 if(this.activePanel.afterSlide){
34124                     this.activePanel.afterSlide();
34125                 }
34126             }
34127         }
34128     },
34129
34130     initAutoHide : function(){
34131         if(this.autoHide !== false){
34132             if(!this.autoHideHd){
34133                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34134                 this.autoHideHd = {
34135                     "mouseout": function(e){
34136                         if(!e.within(this.el, true)){
34137                             st.delay(500);
34138                         }
34139                     },
34140                     "mouseover" : function(e){
34141                         st.cancel();
34142                     },
34143                     scope : this
34144                 };
34145             }
34146             this.el.on(this.autoHideHd);
34147         }
34148     },
34149
34150     clearAutoHide : function(){
34151         if(this.autoHide !== false){
34152             this.el.un("mouseout", this.autoHideHd.mouseout);
34153             this.el.un("mouseover", this.autoHideHd.mouseover);
34154         }
34155     },
34156
34157     clearMonitor : function(){
34158         Roo.get(document).un("click", this.slideInIf, this);
34159     },
34160
34161     // these names are backwards but not changed for compat
34162     slideOut : function(){
34163         if(this.isSlid || this.el.hasActiveFx()){
34164             return;
34165         }
34166         this.isSlid = true;
34167         if(this.collapseBtn){
34168             this.collapseBtn.hide();
34169         }
34170         this.closeBtnState = this.closeBtn.getStyle('display');
34171         this.closeBtn.hide();
34172         if(this.stickBtn){
34173             this.stickBtn.show();
34174         }
34175         this.el.show();
34176         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34177         this.beforeSlide();
34178         this.el.setStyle("z-index", 10001);
34179         this.el.slideIn(this.getSlideAnchor(), {
34180             callback: function(){
34181                 this.afterSlide();
34182                 this.initAutoHide();
34183                 Roo.get(document).on("click", this.slideInIf, this);
34184                 this.fireEvent("slideshow", this);
34185             },
34186             scope: this,
34187             block: true
34188         });
34189     },
34190
34191     afterSlideIn : function(){
34192         this.clearAutoHide();
34193         this.isSlid = false;
34194         this.clearMonitor();
34195         this.el.setStyle("z-index", "");
34196         if(this.collapseBtn){
34197             this.collapseBtn.show();
34198         }
34199         this.closeBtn.setStyle('display', this.closeBtnState);
34200         if(this.stickBtn){
34201             this.stickBtn.hide();
34202         }
34203         this.fireEvent("slidehide", this);
34204     },
34205
34206     slideIn : function(cb){
34207         if(!this.isSlid || this.el.hasActiveFx()){
34208             Roo.callback(cb);
34209             return;
34210         }
34211         this.isSlid = false;
34212         this.beforeSlide();
34213         this.el.slideOut(this.getSlideAnchor(), {
34214             callback: function(){
34215                 this.el.setLeftTop(-10000, -10000);
34216                 this.afterSlide();
34217                 this.afterSlideIn();
34218                 Roo.callback(cb);
34219             },
34220             scope: this,
34221             block: true
34222         });
34223     },
34224     
34225     slideInIf : function(e){
34226         if(!e.within(this.el)){
34227             this.slideIn();
34228         }
34229     },
34230
34231     animateCollapse : function(){
34232         this.beforeSlide();
34233         this.el.setStyle("z-index", 20000);
34234         var anchor = this.getSlideAnchor();
34235         this.el.slideOut(anchor, {
34236             callback : function(){
34237                 this.el.setStyle("z-index", "");
34238                 this.collapsedEl.slideIn(anchor, {duration:.3});
34239                 this.afterSlide();
34240                 this.el.setLocation(-10000,-10000);
34241                 this.el.hide();
34242                 this.fireEvent("collapsed", this);
34243             },
34244             scope: this,
34245             block: true
34246         });
34247     },
34248
34249     animateExpand : function(){
34250         this.beforeSlide();
34251         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34252         this.el.setStyle("z-index", 20000);
34253         this.collapsedEl.hide({
34254             duration:.1
34255         });
34256         this.el.slideIn(this.getSlideAnchor(), {
34257             callback : function(){
34258                 this.el.setStyle("z-index", "");
34259                 this.afterSlide();
34260                 if(this.split){
34261                     this.split.el.show();
34262                 }
34263                 this.fireEvent("invalidated", this);
34264                 this.fireEvent("expanded", this);
34265             },
34266             scope: this,
34267             block: true
34268         });
34269     },
34270
34271     anchors : {
34272         "west" : "left",
34273         "east" : "right",
34274         "north" : "top",
34275         "south" : "bottom"
34276     },
34277
34278     sanchors : {
34279         "west" : "l",
34280         "east" : "r",
34281         "north" : "t",
34282         "south" : "b"
34283     },
34284
34285     canchors : {
34286         "west" : "tl-tr",
34287         "east" : "tr-tl",
34288         "north" : "tl-bl",
34289         "south" : "bl-tl"
34290     },
34291
34292     getAnchor : function(){
34293         return this.anchors[this.position];
34294     },
34295
34296     getCollapseAnchor : function(){
34297         return this.canchors[this.position];
34298     },
34299
34300     getSlideAnchor : function(){
34301         return this.sanchors[this.position];
34302     },
34303
34304     getAlignAdj : function(){
34305         var cm = this.cmargins;
34306         switch(this.position){
34307             case "west":
34308                 return [0, 0];
34309             break;
34310             case "east":
34311                 return [0, 0];
34312             break;
34313             case "north":
34314                 return [0, 0];
34315             break;
34316             case "south":
34317                 return [0, 0];
34318             break;
34319         }
34320     },
34321
34322     getExpandAdj : function(){
34323         var c = this.collapsedEl, cm = this.cmargins;
34324         switch(this.position){
34325             case "west":
34326                 return [-(cm.right+c.getWidth()+cm.left), 0];
34327             break;
34328             case "east":
34329                 return [cm.right+c.getWidth()+cm.left, 0];
34330             break;
34331             case "north":
34332                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34333             break;
34334             case "south":
34335                 return [0, cm.top+cm.bottom+c.getHeight()];
34336             break;
34337         }
34338     }
34339 });/*
34340  * Based on:
34341  * Ext JS Library 1.1.1
34342  * Copyright(c) 2006-2007, Ext JS, LLC.
34343  *
34344  * Originally Released Under LGPL - original licence link has changed is not relivant.
34345  *
34346  * Fork - LGPL
34347  * <script type="text/javascript">
34348  */
34349 /*
34350  * These classes are private internal classes
34351  */
34352 Roo.CenterLayoutRegion = function(mgr, config){
34353     Roo.LayoutRegion.call(this, mgr, config, "center");
34354     this.visible = true;
34355     this.minWidth = config.minWidth || 20;
34356     this.minHeight = config.minHeight || 20;
34357 };
34358
34359 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34360     hide : function(){
34361         // center panel can't be hidden
34362     },
34363     
34364     show : function(){
34365         // center panel can't be hidden
34366     },
34367     
34368     getMinWidth: function(){
34369         return this.minWidth;
34370     },
34371     
34372     getMinHeight: function(){
34373         return this.minHeight;
34374     }
34375 });
34376
34377
34378 Roo.NorthLayoutRegion = function(mgr, config){
34379     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34380     if(this.split){
34381         this.split.placement = Roo.SplitBar.TOP;
34382         this.split.orientation = Roo.SplitBar.VERTICAL;
34383         this.split.el.addClass("x-layout-split-v");
34384     }
34385     var size = config.initialSize || config.height;
34386     if(typeof size != "undefined"){
34387         this.el.setHeight(size);
34388     }
34389 };
34390 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34391     orientation: Roo.SplitBar.VERTICAL,
34392     getBox : function(){
34393         if(this.collapsed){
34394             return this.collapsedEl.getBox();
34395         }
34396         var box = this.el.getBox();
34397         if(this.split){
34398             box.height += this.split.el.getHeight();
34399         }
34400         return box;
34401     },
34402     
34403     updateBox : function(box){
34404         if(this.split && !this.collapsed){
34405             box.height -= this.split.el.getHeight();
34406             this.split.el.setLeft(box.x);
34407             this.split.el.setTop(box.y+box.height);
34408             this.split.el.setWidth(box.width);
34409         }
34410         if(this.collapsed){
34411             this.updateBody(box.width, null);
34412         }
34413         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34414     }
34415 });
34416
34417 Roo.SouthLayoutRegion = function(mgr, config){
34418     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34419     if(this.split){
34420         this.split.placement = Roo.SplitBar.BOTTOM;
34421         this.split.orientation = Roo.SplitBar.VERTICAL;
34422         this.split.el.addClass("x-layout-split-v");
34423     }
34424     var size = config.initialSize || config.height;
34425     if(typeof size != "undefined"){
34426         this.el.setHeight(size);
34427     }
34428 };
34429 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34430     orientation: Roo.SplitBar.VERTICAL,
34431     getBox : function(){
34432         if(this.collapsed){
34433             return this.collapsedEl.getBox();
34434         }
34435         var box = this.el.getBox();
34436         if(this.split){
34437             var sh = this.split.el.getHeight();
34438             box.height += sh;
34439             box.y -= sh;
34440         }
34441         return box;
34442     },
34443     
34444     updateBox : function(box){
34445         if(this.split && !this.collapsed){
34446             var sh = this.split.el.getHeight();
34447             box.height -= sh;
34448             box.y += sh;
34449             this.split.el.setLeft(box.x);
34450             this.split.el.setTop(box.y-sh);
34451             this.split.el.setWidth(box.width);
34452         }
34453         if(this.collapsed){
34454             this.updateBody(box.width, null);
34455         }
34456         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34457     }
34458 });
34459
34460 Roo.EastLayoutRegion = function(mgr, config){
34461     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34462     if(this.split){
34463         this.split.placement = Roo.SplitBar.RIGHT;
34464         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34465         this.split.el.addClass("x-layout-split-h");
34466     }
34467     var size = config.initialSize || config.width;
34468     if(typeof size != "undefined"){
34469         this.el.setWidth(size);
34470     }
34471 };
34472 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34473     orientation: Roo.SplitBar.HORIZONTAL,
34474     getBox : function(){
34475         if(this.collapsed){
34476             return this.collapsedEl.getBox();
34477         }
34478         var box = this.el.getBox();
34479         if(this.split){
34480             var sw = this.split.el.getWidth();
34481             box.width += sw;
34482             box.x -= sw;
34483         }
34484         return box;
34485     },
34486
34487     updateBox : function(box){
34488         if(this.split && !this.collapsed){
34489             var sw = this.split.el.getWidth();
34490             box.width -= sw;
34491             this.split.el.setLeft(box.x);
34492             this.split.el.setTop(box.y);
34493             this.split.el.setHeight(box.height);
34494             box.x += sw;
34495         }
34496         if(this.collapsed){
34497             this.updateBody(null, box.height);
34498         }
34499         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34500     }
34501 });
34502
34503 Roo.WestLayoutRegion = function(mgr, config){
34504     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34505     if(this.split){
34506         this.split.placement = Roo.SplitBar.LEFT;
34507         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34508         this.split.el.addClass("x-layout-split-h");
34509     }
34510     var size = config.initialSize || config.width;
34511     if(typeof size != "undefined"){
34512         this.el.setWidth(size);
34513     }
34514 };
34515 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34516     orientation: Roo.SplitBar.HORIZONTAL,
34517     getBox : function(){
34518         if(this.collapsed){
34519             return this.collapsedEl.getBox();
34520         }
34521         var box = this.el.getBox();
34522         if(this.split){
34523             box.width += this.split.el.getWidth();
34524         }
34525         return box;
34526     },
34527     
34528     updateBox : function(box){
34529         if(this.split && !this.collapsed){
34530             var sw = this.split.el.getWidth();
34531             box.width -= sw;
34532             this.split.el.setLeft(box.x+box.width);
34533             this.split.el.setTop(box.y);
34534             this.split.el.setHeight(box.height);
34535         }
34536         if(this.collapsed){
34537             this.updateBody(null, box.height);
34538         }
34539         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34540     }
34541 });
34542 /*
34543  * Based on:
34544  * Ext JS Library 1.1.1
34545  * Copyright(c) 2006-2007, Ext JS, LLC.
34546  *
34547  * Originally Released Under LGPL - original licence link has changed is not relivant.
34548  *
34549  * Fork - LGPL
34550  * <script type="text/javascript">
34551  */
34552  
34553  
34554 /*
34555  * Private internal class for reading and applying state
34556  */
34557 Roo.LayoutStateManager = function(layout){
34558      // default empty state
34559      this.state = {
34560         north: {},
34561         south: {},
34562         east: {},
34563         west: {}       
34564     };
34565 };
34566
34567 Roo.LayoutStateManager.prototype = {
34568     init : function(layout, provider){
34569         this.provider = provider;
34570         var state = provider.get(layout.id+"-layout-state");
34571         if(state){
34572             var wasUpdating = layout.isUpdating();
34573             if(!wasUpdating){
34574                 layout.beginUpdate();
34575             }
34576             for(var key in state){
34577                 if(typeof state[key] != "function"){
34578                     var rstate = state[key];
34579                     var r = layout.getRegion(key);
34580                     if(r && rstate){
34581                         if(rstate.size){
34582                             r.resizeTo(rstate.size);
34583                         }
34584                         if(rstate.collapsed == true){
34585                             r.collapse(true);
34586                         }else{
34587                             r.expand(null, true);
34588                         }
34589                     }
34590                 }
34591             }
34592             if(!wasUpdating){
34593                 layout.endUpdate();
34594             }
34595             this.state = state; 
34596         }
34597         this.layout = layout;
34598         layout.on("regionresized", this.onRegionResized, this);
34599         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34600         layout.on("regionexpanded", this.onRegionExpanded, this);
34601     },
34602     
34603     storeState : function(){
34604         this.provider.set(this.layout.id+"-layout-state", this.state);
34605     },
34606     
34607     onRegionResized : function(region, newSize){
34608         this.state[region.getPosition()].size = newSize;
34609         this.storeState();
34610     },
34611     
34612     onRegionCollapsed : function(region){
34613         this.state[region.getPosition()].collapsed = true;
34614         this.storeState();
34615     },
34616     
34617     onRegionExpanded : function(region){
34618         this.state[region.getPosition()].collapsed = false;
34619         this.storeState();
34620     }
34621 };/*
34622  * Based on:
34623  * Ext JS Library 1.1.1
34624  * Copyright(c) 2006-2007, Ext JS, LLC.
34625  *
34626  * Originally Released Under LGPL - original licence link has changed is not relivant.
34627  *
34628  * Fork - LGPL
34629  * <script type="text/javascript">
34630  */
34631 /**
34632  * @class Roo.ContentPanel
34633  * @extends Roo.util.Observable
34634  * A basic ContentPanel element.
34635  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34636  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34637  * @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
34638  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34639  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34640  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34641  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34642  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34643  * @cfg {String} title          The title for this panel
34644  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34645  * @cfg {String} url            Calls {@link #setUrl} with this value
34646  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34647  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34648  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34649  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34650
34651  * @constructor
34652  * Create a new ContentPanel.
34653  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34654  * @param {String/Object} config A string to set only the title or a config object
34655  * @param {String} content (optional) Set the HTML content for this panel
34656  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34657  */
34658 Roo.ContentPanel = function(el, config, content){
34659     
34660      
34661     /*
34662     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34663         config = el;
34664         el = Roo.id();
34665     }
34666     if (config && config.parentLayout) { 
34667         el = config.parentLayout.el.createChild(); 
34668     }
34669     */
34670     if(el.autoCreate){ // xtype is available if this is called from factory
34671         config = el;
34672         el = Roo.id();
34673     }
34674     this.el = Roo.get(el);
34675     if(!this.el && config && config.autoCreate){
34676         if(typeof config.autoCreate == "object"){
34677             if(!config.autoCreate.id){
34678                 config.autoCreate.id = config.id||el;
34679             }
34680             this.el = Roo.DomHelper.append(document.body,
34681                         config.autoCreate, true);
34682         }else{
34683             this.el = Roo.DomHelper.append(document.body,
34684                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34685         }
34686     }
34687     this.closable = false;
34688     this.loaded = false;
34689     this.active = false;
34690     if(typeof config == "string"){
34691         this.title = config;
34692     }else{
34693         Roo.apply(this, config);
34694     }
34695     
34696     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34697         this.wrapEl = this.el.wrap();
34698         this.toolbar.container = this.el.insertSibling(false, 'before');
34699         this.toolbar = new Roo.Toolbar(this.toolbar);
34700     }
34701     
34702     // xtype created footer. - not sure if will work as we normally have to render first..
34703     if (this.footer && !this.footer.el && this.footer.xtype) {
34704         if (!this.wrapEl) {
34705             this.wrapEl = this.el.wrap();
34706         }
34707     
34708         this.footer.container = this.wrapEl.createChild();
34709          
34710         this.footer = Roo.factory(this.footer, Roo);
34711         
34712     }
34713     
34714     if(this.resizeEl){
34715         this.resizeEl = Roo.get(this.resizeEl, true);
34716     }else{
34717         this.resizeEl = this.el;
34718     }
34719     // handle view.xtype
34720     
34721  
34722     
34723     
34724     this.addEvents({
34725         /**
34726          * @event activate
34727          * Fires when this panel is activated. 
34728          * @param {Roo.ContentPanel} this
34729          */
34730         "activate" : true,
34731         /**
34732          * @event deactivate
34733          * Fires when this panel is activated. 
34734          * @param {Roo.ContentPanel} this
34735          */
34736         "deactivate" : true,
34737
34738         /**
34739          * @event resize
34740          * Fires when this panel is resized if fitToFrame is true.
34741          * @param {Roo.ContentPanel} this
34742          * @param {Number} width The width after any component adjustments
34743          * @param {Number} height The height after any component adjustments
34744          */
34745         "resize" : true,
34746         
34747          /**
34748          * @event render
34749          * Fires when this tab is created
34750          * @param {Roo.ContentPanel} this
34751          */
34752         "render" : true
34753         
34754         
34755         
34756     });
34757     
34758
34759     
34760     
34761     if(this.autoScroll){
34762         this.resizeEl.setStyle("overflow", "auto");
34763     } else {
34764         // fix randome scrolling
34765         this.el.on('scroll', function() {
34766             Roo.log('fix random scolling');
34767             this.scrollTo('top',0); 
34768         });
34769     }
34770     content = content || this.content;
34771     if(content){
34772         this.setContent(content);
34773     }
34774     if(config && config.url){
34775         this.setUrl(this.url, this.params, this.loadOnce);
34776     }
34777     
34778     
34779     
34780     Roo.ContentPanel.superclass.constructor.call(this);
34781     
34782     if (this.view && typeof(this.view.xtype) != 'undefined') {
34783         this.view.el = this.el.appendChild(document.createElement("div"));
34784         this.view = Roo.factory(this.view); 
34785         this.view.render  &&  this.view.render(false, '');  
34786     }
34787     
34788     
34789     this.fireEvent('render', this);
34790 };
34791
34792 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34793     tabTip:'',
34794     setRegion : function(region){
34795         this.region = region;
34796         if(region){
34797            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34798         }else{
34799            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34800         } 
34801     },
34802     
34803     /**
34804      * Returns the toolbar for this Panel if one was configured. 
34805      * @return {Roo.Toolbar} 
34806      */
34807     getToolbar : function(){
34808         return this.toolbar;
34809     },
34810     
34811     setActiveState : function(active){
34812         this.active = active;
34813         if(!active){
34814             this.fireEvent("deactivate", this);
34815         }else{
34816             this.fireEvent("activate", this);
34817         }
34818     },
34819     /**
34820      * Updates this panel's element
34821      * @param {String} content The new content
34822      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34823     */
34824     setContent : function(content, loadScripts){
34825         this.el.update(content, loadScripts);
34826     },
34827
34828     ignoreResize : function(w, h){
34829         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34830             return true;
34831         }else{
34832             this.lastSize = {width: w, height: h};
34833             return false;
34834         }
34835     },
34836     /**
34837      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34838      * @return {Roo.UpdateManager} The UpdateManager
34839      */
34840     getUpdateManager : function(){
34841         return this.el.getUpdateManager();
34842     },
34843      /**
34844      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34845      * @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:
34846 <pre><code>
34847 panel.load({
34848     url: "your-url.php",
34849     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34850     callback: yourFunction,
34851     scope: yourObject, //(optional scope)
34852     discardUrl: false,
34853     nocache: false,
34854     text: "Loading...",
34855     timeout: 30,
34856     scripts: false
34857 });
34858 </code></pre>
34859      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34860      * 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.
34861      * @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}
34862      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34863      * @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.
34864      * @return {Roo.ContentPanel} this
34865      */
34866     load : function(){
34867         var um = this.el.getUpdateManager();
34868         um.update.apply(um, arguments);
34869         return this;
34870     },
34871
34872
34873     /**
34874      * 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.
34875      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34876      * @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)
34877      * @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)
34878      * @return {Roo.UpdateManager} The UpdateManager
34879      */
34880     setUrl : function(url, params, loadOnce){
34881         if(this.refreshDelegate){
34882             this.removeListener("activate", this.refreshDelegate);
34883         }
34884         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34885         this.on("activate", this.refreshDelegate);
34886         return this.el.getUpdateManager();
34887     },
34888     
34889     _handleRefresh : function(url, params, loadOnce){
34890         if(!loadOnce || !this.loaded){
34891             var updater = this.el.getUpdateManager();
34892             updater.update(url, params, this._setLoaded.createDelegate(this));
34893         }
34894     },
34895     
34896     _setLoaded : function(){
34897         this.loaded = true;
34898     }, 
34899     
34900     /**
34901      * Returns this panel's id
34902      * @return {String} 
34903      */
34904     getId : function(){
34905         return this.el.id;
34906     },
34907     
34908     /** 
34909      * Returns this panel's element - used by regiosn to add.
34910      * @return {Roo.Element} 
34911      */
34912     getEl : function(){
34913         return this.wrapEl || this.el;
34914     },
34915     
34916     adjustForComponents : function(width, height)
34917     {
34918         //Roo.log('adjustForComponents ');
34919         if(this.resizeEl != this.el){
34920             width -= this.el.getFrameWidth('lr');
34921             height -= this.el.getFrameWidth('tb');
34922         }
34923         if(this.toolbar){
34924             var te = this.toolbar.getEl();
34925             height -= te.getHeight();
34926             te.setWidth(width);
34927         }
34928         if(this.footer){
34929             var te = this.footer.getEl();
34930             Roo.log("footer:" + te.getHeight());
34931             
34932             height -= te.getHeight();
34933             te.setWidth(width);
34934         }
34935         
34936         
34937         if(this.adjustments){
34938             width += this.adjustments[0];
34939             height += this.adjustments[1];
34940         }
34941         return {"width": width, "height": height};
34942     },
34943     
34944     setSize : function(width, height){
34945         if(this.fitToFrame && !this.ignoreResize(width, height)){
34946             if(this.fitContainer && this.resizeEl != this.el){
34947                 this.el.setSize(width, height);
34948             }
34949             var size = this.adjustForComponents(width, height);
34950             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34951             this.fireEvent('resize', this, size.width, size.height);
34952         }
34953     },
34954     
34955     /**
34956      * Returns this panel's title
34957      * @return {String} 
34958      */
34959     getTitle : function(){
34960         return this.title;
34961     },
34962     
34963     /**
34964      * Set this panel's title
34965      * @param {String} title
34966      */
34967     setTitle : function(title){
34968         this.title = title;
34969         if(this.region){
34970             this.region.updatePanelTitle(this, title);
34971         }
34972     },
34973     
34974     /**
34975      * Returns true is this panel was configured to be closable
34976      * @return {Boolean} 
34977      */
34978     isClosable : function(){
34979         return this.closable;
34980     },
34981     
34982     beforeSlide : function(){
34983         this.el.clip();
34984         this.resizeEl.clip();
34985     },
34986     
34987     afterSlide : function(){
34988         this.el.unclip();
34989         this.resizeEl.unclip();
34990     },
34991     
34992     /**
34993      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34994      *   Will fail silently if the {@link #setUrl} method has not been called.
34995      *   This does not activate the panel, just updates its content.
34996      */
34997     refresh : function(){
34998         if(this.refreshDelegate){
34999            this.loaded = false;
35000            this.refreshDelegate();
35001         }
35002     },
35003     
35004     /**
35005      * Destroys this panel
35006      */
35007     destroy : function(){
35008         this.el.removeAllListeners();
35009         var tempEl = document.createElement("span");
35010         tempEl.appendChild(this.el.dom);
35011         tempEl.innerHTML = "";
35012         this.el.remove();
35013         this.el = null;
35014     },
35015     
35016     /**
35017      * form - if the content panel contains a form - this is a reference to it.
35018      * @type {Roo.form.Form}
35019      */
35020     form : false,
35021     /**
35022      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35023      *    This contains a reference to it.
35024      * @type {Roo.View}
35025      */
35026     view : false,
35027     
35028       /**
35029      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35030      * <pre><code>
35031
35032 layout.addxtype({
35033        xtype : 'Form',
35034        items: [ .... ]
35035    }
35036 );
35037
35038 </code></pre>
35039      * @param {Object} cfg Xtype definition of item to add.
35040      */
35041     
35042     addxtype : function(cfg) {
35043         // add form..
35044         if (cfg.xtype.match(/^Form$/)) {
35045             
35046             var el;
35047             //if (this.footer) {
35048             //    el = this.footer.container.insertSibling(false, 'before');
35049             //} else {
35050                 el = this.el.createChild();
35051             //}
35052
35053             this.form = new  Roo.form.Form(cfg);
35054             
35055             
35056             if ( this.form.allItems.length) this.form.render(el.dom);
35057             return this.form;
35058         }
35059         // should only have one of theses..
35060         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35061             // views.. should not be just added - used named prop 'view''
35062             
35063             cfg.el = this.el.appendChild(document.createElement("div"));
35064             // factory?
35065             
35066             var ret = new Roo.factory(cfg);
35067              
35068              ret.render && ret.render(false, ''); // render blank..
35069             this.view = ret;
35070             return ret;
35071         }
35072         return false;
35073     }
35074 });
35075
35076 /**
35077  * @class Roo.GridPanel
35078  * @extends Roo.ContentPanel
35079  * @constructor
35080  * Create a new GridPanel.
35081  * @param {Roo.grid.Grid} grid The grid for this panel
35082  * @param {String/Object} config A string to set only the panel's title, or a config object
35083  */
35084 Roo.GridPanel = function(grid, config){
35085     
35086   
35087     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35088         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35089         
35090     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35091     
35092     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35093     
35094     if(this.toolbar){
35095         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35096     }
35097     // xtype created footer. - not sure if will work as we normally have to render first..
35098     if (this.footer && !this.footer.el && this.footer.xtype) {
35099         
35100         this.footer.container = this.grid.getView().getFooterPanel(true);
35101         this.footer.dataSource = this.grid.dataSource;
35102         this.footer = Roo.factory(this.footer, Roo);
35103         
35104     }
35105     
35106     grid.monitorWindowResize = false; // turn off autosizing
35107     grid.autoHeight = false;
35108     grid.autoWidth = false;
35109     this.grid = grid;
35110     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35111 };
35112
35113 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35114     getId : function(){
35115         return this.grid.id;
35116     },
35117     
35118     /**
35119      * Returns the grid for this panel
35120      * @return {Roo.grid.Grid} 
35121      */
35122     getGrid : function(){
35123         return this.grid;    
35124     },
35125     
35126     setSize : function(width, height){
35127         if(!this.ignoreResize(width, height)){
35128             var grid = this.grid;
35129             var size = this.adjustForComponents(width, height);
35130             grid.getGridEl().setSize(size.width, size.height);
35131             grid.autoSize();
35132         }
35133     },
35134     
35135     beforeSlide : function(){
35136         this.grid.getView().scroller.clip();
35137     },
35138     
35139     afterSlide : function(){
35140         this.grid.getView().scroller.unclip();
35141     },
35142     
35143     destroy : function(){
35144         this.grid.destroy();
35145         delete this.grid;
35146         Roo.GridPanel.superclass.destroy.call(this); 
35147     }
35148 });
35149
35150
35151 /**
35152  * @class Roo.NestedLayoutPanel
35153  * @extends Roo.ContentPanel
35154  * @constructor
35155  * Create a new NestedLayoutPanel.
35156  * 
35157  * 
35158  * @param {Roo.BorderLayout} layout The layout for this panel
35159  * @param {String/Object} config A string to set only the title or a config object
35160  */
35161 Roo.NestedLayoutPanel = function(layout, config)
35162 {
35163     // construct with only one argument..
35164     /* FIXME - implement nicer consturctors
35165     if (layout.layout) {
35166         config = layout;
35167         layout = config.layout;
35168         delete config.layout;
35169     }
35170     if (layout.xtype && !layout.getEl) {
35171         // then layout needs constructing..
35172         layout = Roo.factory(layout, Roo);
35173     }
35174     */
35175     
35176     
35177     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35178     
35179     layout.monitorWindowResize = false; // turn off autosizing
35180     this.layout = layout;
35181     this.layout.getEl().addClass("x-layout-nested-layout");
35182     
35183     
35184     
35185     
35186 };
35187
35188 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35189
35190     setSize : function(width, height){
35191         if(!this.ignoreResize(width, height)){
35192             var size = this.adjustForComponents(width, height);
35193             var el = this.layout.getEl();
35194             el.setSize(size.width, size.height);
35195             var touch = el.dom.offsetWidth;
35196             this.layout.layout();
35197             // ie requires a double layout on the first pass
35198             if(Roo.isIE && !this.initialized){
35199                 this.initialized = true;
35200                 this.layout.layout();
35201             }
35202         }
35203     },
35204     
35205     // activate all subpanels if not currently active..
35206     
35207     setActiveState : function(active){
35208         this.active = active;
35209         if(!active){
35210             this.fireEvent("deactivate", this);
35211             return;
35212         }
35213         
35214         this.fireEvent("activate", this);
35215         // not sure if this should happen before or after..
35216         if (!this.layout) {
35217             return; // should not happen..
35218         }
35219         var reg = false;
35220         for (var r in this.layout.regions) {
35221             reg = this.layout.getRegion(r);
35222             if (reg.getActivePanel()) {
35223                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35224                 reg.setActivePanel(reg.getActivePanel());
35225                 continue;
35226             }
35227             if (!reg.panels.length) {
35228                 continue;
35229             }
35230             reg.showPanel(reg.getPanel(0));
35231         }
35232         
35233         
35234         
35235         
35236     },
35237     
35238     /**
35239      * Returns the nested BorderLayout for this panel
35240      * @return {Roo.BorderLayout} 
35241      */
35242     getLayout : function(){
35243         return this.layout;
35244     },
35245     
35246      /**
35247      * Adds a xtype elements to the layout of the nested panel
35248      * <pre><code>
35249
35250 panel.addxtype({
35251        xtype : 'ContentPanel',
35252        region: 'west',
35253        items: [ .... ]
35254    }
35255 );
35256
35257 panel.addxtype({
35258         xtype : 'NestedLayoutPanel',
35259         region: 'west',
35260         layout: {
35261            center: { },
35262            west: { }   
35263         },
35264         items : [ ... list of content panels or nested layout panels.. ]
35265    }
35266 );
35267 </code></pre>
35268      * @param {Object} cfg Xtype definition of item to add.
35269      */
35270     addxtype : function(cfg) {
35271         return this.layout.addxtype(cfg);
35272     
35273     }
35274 });
35275
35276 Roo.ScrollPanel = function(el, config, content){
35277     config = config || {};
35278     config.fitToFrame = true;
35279     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35280     
35281     this.el.dom.style.overflow = "hidden";
35282     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35283     this.el.removeClass("x-layout-inactive-content");
35284     this.el.on("mousewheel", this.onWheel, this);
35285
35286     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35287     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35288     up.unselectable(); down.unselectable();
35289     up.on("click", this.scrollUp, this);
35290     down.on("click", this.scrollDown, this);
35291     up.addClassOnOver("x-scroller-btn-over");
35292     down.addClassOnOver("x-scroller-btn-over");
35293     up.addClassOnClick("x-scroller-btn-click");
35294     down.addClassOnClick("x-scroller-btn-click");
35295     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35296
35297     this.resizeEl = this.el;
35298     this.el = wrap; this.up = up; this.down = down;
35299 };
35300
35301 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35302     increment : 100,
35303     wheelIncrement : 5,
35304     scrollUp : function(){
35305         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35306     },
35307
35308     scrollDown : function(){
35309         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35310     },
35311
35312     afterScroll : function(){
35313         var el = this.resizeEl;
35314         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35315         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35316         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35317     },
35318
35319     setSize : function(){
35320         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35321         this.afterScroll();
35322     },
35323
35324     onWheel : function(e){
35325         var d = e.getWheelDelta();
35326         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35327         this.afterScroll();
35328         e.stopEvent();
35329     },
35330
35331     setContent : function(content, loadScripts){
35332         this.resizeEl.update(content, loadScripts);
35333     }
35334
35335 });
35336
35337
35338
35339
35340
35341
35342
35343
35344
35345 /**
35346  * @class Roo.TreePanel
35347  * @extends Roo.ContentPanel
35348  * @constructor
35349  * Create a new TreePanel. - defaults to fit/scoll contents.
35350  * @param {String/Object} config A string to set only the panel's title, or a config object
35351  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35352  */
35353 Roo.TreePanel = function(config){
35354     var el = config.el;
35355     var tree = config.tree;
35356     delete config.tree; 
35357     delete config.el; // hopefull!
35358     
35359     // wrapper for IE7 strict & safari scroll issue
35360     
35361     var treeEl = el.createChild();
35362     config.resizeEl = treeEl;
35363     
35364     
35365     
35366     Roo.TreePanel.superclass.constructor.call(this, el, config);
35367  
35368  
35369     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35370     //console.log(tree);
35371     this.on('activate', function()
35372     {
35373         if (this.tree.rendered) {
35374             return;
35375         }
35376         //console.log('render tree');
35377         this.tree.render();
35378     });
35379     // this should not be needed.. - it's actually the 'el' that resizes?
35380     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35381     
35382     //this.on('resize',  function (cp, w, h) {
35383     //        this.tree.innerCt.setWidth(w);
35384     //        this.tree.innerCt.setHeight(h);
35385     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35386     //});
35387
35388         
35389     
35390 };
35391
35392 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35393     fitToFrame : true,
35394     autoScroll : true
35395 });
35396
35397
35398
35399
35400
35401
35402
35403
35404
35405
35406
35407 /*
35408  * Based on:
35409  * Ext JS Library 1.1.1
35410  * Copyright(c) 2006-2007, Ext JS, LLC.
35411  *
35412  * Originally Released Under LGPL - original licence link has changed is not relivant.
35413  *
35414  * Fork - LGPL
35415  * <script type="text/javascript">
35416  */
35417  
35418
35419 /**
35420  * @class Roo.ReaderLayout
35421  * @extends Roo.BorderLayout
35422  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35423  * center region containing two nested regions (a top one for a list view and one for item preview below),
35424  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35425  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35426  * expedites the setup of the overall layout and regions for this common application style.
35427  * Example:
35428  <pre><code>
35429 var reader = new Roo.ReaderLayout();
35430 var CP = Roo.ContentPanel;  // shortcut for adding
35431
35432 reader.beginUpdate();
35433 reader.add("north", new CP("north", "North"));
35434 reader.add("west", new CP("west", {title: "West"}));
35435 reader.add("east", new CP("east", {title: "East"}));
35436
35437 reader.regions.listView.add(new CP("listView", "List"));
35438 reader.regions.preview.add(new CP("preview", "Preview"));
35439 reader.endUpdate();
35440 </code></pre>
35441 * @constructor
35442 * Create a new ReaderLayout
35443 * @param {Object} config Configuration options
35444 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35445 * document.body if omitted)
35446 */
35447 Roo.ReaderLayout = function(config, renderTo){
35448     var c = config || {size:{}};
35449     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35450         north: c.north !== false ? Roo.apply({
35451             split:false,
35452             initialSize: 32,
35453             titlebar: false
35454         }, c.north) : false,
35455         west: c.west !== false ? Roo.apply({
35456             split:true,
35457             initialSize: 200,
35458             minSize: 175,
35459             maxSize: 400,
35460             titlebar: true,
35461             collapsible: true,
35462             animate: true,
35463             margins:{left:5,right:0,bottom:5,top:5},
35464             cmargins:{left:5,right:5,bottom:5,top:5}
35465         }, c.west) : false,
35466         east: c.east !== false ? Roo.apply({
35467             split:true,
35468             initialSize: 200,
35469             minSize: 175,
35470             maxSize: 400,
35471             titlebar: true,
35472             collapsible: true,
35473             animate: true,
35474             margins:{left:0,right:5,bottom:5,top:5},
35475             cmargins:{left:5,right:5,bottom:5,top:5}
35476         }, c.east) : false,
35477         center: Roo.apply({
35478             tabPosition: 'top',
35479             autoScroll:false,
35480             closeOnTab: true,
35481             titlebar:false,
35482             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35483         }, c.center)
35484     });
35485
35486     this.el.addClass('x-reader');
35487
35488     this.beginUpdate();
35489
35490     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35491         south: c.preview !== false ? Roo.apply({
35492             split:true,
35493             initialSize: 200,
35494             minSize: 100,
35495             autoScroll:true,
35496             collapsible:true,
35497             titlebar: true,
35498             cmargins:{top:5,left:0, right:0, bottom:0}
35499         }, c.preview) : false,
35500         center: Roo.apply({
35501             autoScroll:false,
35502             titlebar:false,
35503             minHeight:200
35504         }, c.listView)
35505     });
35506     this.add('center', new Roo.NestedLayoutPanel(inner,
35507             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35508
35509     this.endUpdate();
35510
35511     this.regions.preview = inner.getRegion('south');
35512     this.regions.listView = inner.getRegion('center');
35513 };
35514
35515 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35516  * Based on:
35517  * Ext JS Library 1.1.1
35518  * Copyright(c) 2006-2007, Ext JS, LLC.
35519  *
35520  * Originally Released Under LGPL - original licence link has changed is not relivant.
35521  *
35522  * Fork - LGPL
35523  * <script type="text/javascript">
35524  */
35525  
35526 /**
35527  * @class Roo.grid.Grid
35528  * @extends Roo.util.Observable
35529  * This class represents the primary interface of a component based grid control.
35530  * <br><br>Usage:<pre><code>
35531  var grid = new Roo.grid.Grid("my-container-id", {
35532      ds: myDataStore,
35533      cm: myColModel,
35534      selModel: mySelectionModel,
35535      autoSizeColumns: true,
35536      monitorWindowResize: false,
35537      trackMouseOver: true
35538  });
35539  // set any options
35540  grid.render();
35541  * </code></pre>
35542  * <b>Common Problems:</b><br/>
35543  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35544  * element will correct this<br/>
35545  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35546  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35547  * are unpredictable.<br/>
35548  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35549  * grid to calculate dimensions/offsets.<br/>
35550   * @constructor
35551  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35552  * The container MUST have some type of size defined for the grid to fill. The container will be
35553  * automatically set to position relative if it isn't already.
35554  * @param {Object} config A config object that sets properties on this grid.
35555  */
35556 Roo.grid.Grid = function(container, config){
35557         // initialize the container
35558         this.container = Roo.get(container);
35559         this.container.update("");
35560         this.container.setStyle("overflow", "hidden");
35561     this.container.addClass('x-grid-container');
35562
35563     this.id = this.container.id;
35564
35565     Roo.apply(this, config);
35566     // check and correct shorthanded configs
35567     if(this.ds){
35568         this.dataSource = this.ds;
35569         delete this.ds;
35570     }
35571     if(this.cm){
35572         this.colModel = this.cm;
35573         delete this.cm;
35574     }
35575     if(this.sm){
35576         this.selModel = this.sm;
35577         delete this.sm;
35578     }
35579
35580     if (this.selModel) {
35581         this.selModel = Roo.factory(this.selModel, Roo.grid);
35582         this.sm = this.selModel;
35583         this.sm.xmodule = this.xmodule || false;
35584     }
35585     if (typeof(this.colModel.config) == 'undefined') {
35586         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35587         this.cm = this.colModel;
35588         this.cm.xmodule = this.xmodule || false;
35589     }
35590     if (this.dataSource) {
35591         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35592         this.ds = this.dataSource;
35593         this.ds.xmodule = this.xmodule || false;
35594          
35595     }
35596     
35597     
35598     
35599     if(this.width){
35600         this.container.setWidth(this.width);
35601     }
35602
35603     if(this.height){
35604         this.container.setHeight(this.height);
35605     }
35606     /** @private */
35607         this.addEvents({
35608         // raw events
35609         /**
35610          * @event click
35611          * The raw click event for the entire grid.
35612          * @param {Roo.EventObject} e
35613          */
35614         "click" : true,
35615         /**
35616          * @event dblclick
35617          * The raw dblclick event for the entire grid.
35618          * @param {Roo.EventObject} e
35619          */
35620         "dblclick" : true,
35621         /**
35622          * @event contextmenu
35623          * The raw contextmenu event for the entire grid.
35624          * @param {Roo.EventObject} e
35625          */
35626         "contextmenu" : true,
35627         /**
35628          * @event mousedown
35629          * The raw mousedown event for the entire grid.
35630          * @param {Roo.EventObject} e
35631          */
35632         "mousedown" : true,
35633         /**
35634          * @event mouseup
35635          * The raw mouseup event for the entire grid.
35636          * @param {Roo.EventObject} e
35637          */
35638         "mouseup" : true,
35639         /**
35640          * @event mouseover
35641          * The raw mouseover event for the entire grid.
35642          * @param {Roo.EventObject} e
35643          */
35644         "mouseover" : true,
35645         /**
35646          * @event mouseout
35647          * The raw mouseout event for the entire grid.
35648          * @param {Roo.EventObject} e
35649          */
35650         "mouseout" : true,
35651         /**
35652          * @event keypress
35653          * The raw keypress event for the entire grid.
35654          * @param {Roo.EventObject} e
35655          */
35656         "keypress" : true,
35657         /**
35658          * @event keydown
35659          * The raw keydown event for the entire grid.
35660          * @param {Roo.EventObject} e
35661          */
35662         "keydown" : true,
35663
35664         // custom events
35665
35666         /**
35667          * @event cellclick
35668          * Fires when a cell is clicked
35669          * @param {Grid} this
35670          * @param {Number} rowIndex
35671          * @param {Number} columnIndex
35672          * @param {Roo.EventObject} e
35673          */
35674         "cellclick" : true,
35675         /**
35676          * @event celldblclick
35677          * Fires when a cell is double clicked
35678          * @param {Grid} this
35679          * @param {Number} rowIndex
35680          * @param {Number} columnIndex
35681          * @param {Roo.EventObject} e
35682          */
35683         "celldblclick" : true,
35684         /**
35685          * @event rowclick
35686          * Fires when a row is clicked
35687          * @param {Grid} this
35688          * @param {Number} rowIndex
35689          * @param {Roo.EventObject} e
35690          */
35691         "rowclick" : true,
35692         /**
35693          * @event rowdblclick
35694          * Fires when a row is double clicked
35695          * @param {Grid} this
35696          * @param {Number} rowIndex
35697          * @param {Roo.EventObject} e
35698          */
35699         "rowdblclick" : true,
35700         /**
35701          * @event headerclick
35702          * Fires when a header is clicked
35703          * @param {Grid} this
35704          * @param {Number} columnIndex
35705          * @param {Roo.EventObject} e
35706          */
35707         "headerclick" : true,
35708         /**
35709          * @event headerdblclick
35710          * Fires when a header cell is double clicked
35711          * @param {Grid} this
35712          * @param {Number} columnIndex
35713          * @param {Roo.EventObject} e
35714          */
35715         "headerdblclick" : true,
35716         /**
35717          * @event rowcontextmenu
35718          * Fires when a row is right clicked
35719          * @param {Grid} this
35720          * @param {Number} rowIndex
35721          * @param {Roo.EventObject} e
35722          */
35723         "rowcontextmenu" : true,
35724         /**
35725          * @event cellcontextmenu
35726          * Fires when a cell is right clicked
35727          * @param {Grid} this
35728          * @param {Number} rowIndex
35729          * @param {Number} cellIndex
35730          * @param {Roo.EventObject} e
35731          */
35732          "cellcontextmenu" : true,
35733         /**
35734          * @event headercontextmenu
35735          * Fires when a header is right clicked
35736          * @param {Grid} this
35737          * @param {Number} columnIndex
35738          * @param {Roo.EventObject} e
35739          */
35740         "headercontextmenu" : true,
35741         /**
35742          * @event bodyscroll
35743          * Fires when the body element is scrolled
35744          * @param {Number} scrollLeft
35745          * @param {Number} scrollTop
35746          */
35747         "bodyscroll" : true,
35748         /**
35749          * @event columnresize
35750          * Fires when the user resizes a column
35751          * @param {Number} columnIndex
35752          * @param {Number} newSize
35753          */
35754         "columnresize" : true,
35755         /**
35756          * @event columnmove
35757          * Fires when the user moves a column
35758          * @param {Number} oldIndex
35759          * @param {Number} newIndex
35760          */
35761         "columnmove" : true,
35762         /**
35763          * @event startdrag
35764          * Fires when row(s) start being dragged
35765          * @param {Grid} this
35766          * @param {Roo.GridDD} dd The drag drop object
35767          * @param {event} e The raw browser event
35768          */
35769         "startdrag" : true,
35770         /**
35771          * @event enddrag
35772          * Fires when a drag operation is complete
35773          * @param {Grid} this
35774          * @param {Roo.GridDD} dd The drag drop object
35775          * @param {event} e The raw browser event
35776          */
35777         "enddrag" : true,
35778         /**
35779          * @event dragdrop
35780          * Fires when dragged row(s) are dropped on a valid DD target
35781          * @param {Grid} this
35782          * @param {Roo.GridDD} dd The drag drop object
35783          * @param {String} targetId The target drag drop object
35784          * @param {event} e The raw browser event
35785          */
35786         "dragdrop" : true,
35787         /**
35788          * @event dragover
35789          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35790          * @param {Grid} this
35791          * @param {Roo.GridDD} dd The drag drop object
35792          * @param {String} targetId The target drag drop object
35793          * @param {event} e The raw browser event
35794          */
35795         "dragover" : true,
35796         /**
35797          * @event dragenter
35798          *  Fires when the dragged row(s) first cross another DD target while being dragged
35799          * @param {Grid} this
35800          * @param {Roo.GridDD} dd The drag drop object
35801          * @param {String} targetId The target drag drop object
35802          * @param {event} e The raw browser event
35803          */
35804         "dragenter" : true,
35805         /**
35806          * @event dragout
35807          * Fires when the dragged row(s) leave another DD target while being dragged
35808          * @param {Grid} this
35809          * @param {Roo.GridDD} dd The drag drop object
35810          * @param {String} targetId The target drag drop object
35811          * @param {event} e The raw browser event
35812          */
35813         "dragout" : true,
35814         /**
35815          * @event rowclass
35816          * Fires when a row is rendered, so you can change add a style to it.
35817          * @param {GridView} gridview   The grid view
35818          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35819          */
35820         'rowclass' : true,
35821
35822         /**
35823          * @event render
35824          * Fires when the grid is rendered
35825          * @param {Grid} grid
35826          */
35827         'render' : true
35828     });
35829
35830     Roo.grid.Grid.superclass.constructor.call(this);
35831 };
35832 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35833     
35834     /**
35835      * @cfg {String} ddGroup - drag drop group.
35836      */
35837
35838     /**
35839      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35840      */
35841     minColumnWidth : 25,
35842
35843     /**
35844      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35845      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35846      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35847      */
35848     autoSizeColumns : false,
35849
35850     /**
35851      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35852      */
35853     autoSizeHeaders : true,
35854
35855     /**
35856      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35857      */
35858     monitorWindowResize : true,
35859
35860     /**
35861      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35862      * rows measured to get a columns size. Default is 0 (all rows).
35863      */
35864     maxRowsToMeasure : 0,
35865
35866     /**
35867      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35868      */
35869     trackMouseOver : true,
35870
35871     /**
35872     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35873     */
35874     
35875     /**
35876     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35877     */
35878     enableDragDrop : false,
35879     
35880     /**
35881     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35882     */
35883     enableColumnMove : true,
35884     
35885     /**
35886     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35887     */
35888     enableColumnHide : true,
35889     
35890     /**
35891     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35892     */
35893     enableRowHeightSync : false,
35894     
35895     /**
35896     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35897     */
35898     stripeRows : true,
35899     
35900     /**
35901     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35902     */
35903     autoHeight : false,
35904
35905     /**
35906      * @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.
35907      */
35908     autoExpandColumn : false,
35909
35910     /**
35911     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35912     * Default is 50.
35913     */
35914     autoExpandMin : 50,
35915
35916     /**
35917     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35918     */
35919     autoExpandMax : 1000,
35920
35921     /**
35922     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35923     */
35924     view : null,
35925
35926     /**
35927     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35928     */
35929     loadMask : false,
35930     /**
35931     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35932     */
35933     dropTarget: false,
35934     
35935    
35936     
35937     // private
35938     rendered : false,
35939
35940     /**
35941     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35942     * of a fixed width. Default is false.
35943     */
35944     /**
35945     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35946     */
35947     /**
35948      * Called once after all setup has been completed and the grid is ready to be rendered.
35949      * @return {Roo.grid.Grid} this
35950      */
35951     render : function()
35952     {
35953         var c = this.container;
35954         // try to detect autoHeight/width mode
35955         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35956             this.autoHeight = true;
35957         }
35958         var view = this.getView();
35959         view.init(this);
35960
35961         c.on("click", this.onClick, this);
35962         c.on("dblclick", this.onDblClick, this);
35963         c.on("contextmenu", this.onContextMenu, this);
35964         c.on("keydown", this.onKeyDown, this);
35965         if (Roo.isTouch) {
35966             c.on("touchstart", this.onTouchStart, this);
35967         }
35968
35969         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35970
35971         this.getSelectionModel().init(this);
35972
35973         view.render();
35974
35975         if(this.loadMask){
35976             this.loadMask = new Roo.LoadMask(this.container,
35977                     Roo.apply({store:this.dataSource}, this.loadMask));
35978         }
35979         
35980         
35981         if (this.toolbar && this.toolbar.xtype) {
35982             this.toolbar.container = this.getView().getHeaderPanel(true);
35983             this.toolbar = new Roo.Toolbar(this.toolbar);
35984         }
35985         if (this.footer && this.footer.xtype) {
35986             this.footer.dataSource = this.getDataSource();
35987             this.footer.container = this.getView().getFooterPanel(true);
35988             this.footer = Roo.factory(this.footer, Roo);
35989         }
35990         if (this.dropTarget && this.dropTarget.xtype) {
35991             delete this.dropTarget.xtype;
35992             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35993         }
35994         
35995         
35996         this.rendered = true;
35997         this.fireEvent('render', this);
35998         return this;
35999     },
36000
36001         /**
36002          * Reconfigures the grid to use a different Store and Column Model.
36003          * The View will be bound to the new objects and refreshed.
36004          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36005          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36006          */
36007     reconfigure : function(dataSource, colModel){
36008         if(this.loadMask){
36009             this.loadMask.destroy();
36010             this.loadMask = new Roo.LoadMask(this.container,
36011                     Roo.apply({store:dataSource}, this.loadMask));
36012         }
36013         this.view.bind(dataSource, colModel);
36014         this.dataSource = dataSource;
36015         this.colModel = colModel;
36016         this.view.refresh(true);
36017     },
36018
36019     // private
36020     onKeyDown : function(e){
36021         this.fireEvent("keydown", e);
36022     },
36023
36024     /**
36025      * Destroy this grid.
36026      * @param {Boolean} removeEl True to remove the element
36027      */
36028     destroy : function(removeEl, keepListeners){
36029         if(this.loadMask){
36030             this.loadMask.destroy();
36031         }
36032         var c = this.container;
36033         c.removeAllListeners();
36034         this.view.destroy();
36035         this.colModel.purgeListeners();
36036         if(!keepListeners){
36037             this.purgeListeners();
36038         }
36039         c.update("");
36040         if(removeEl === true){
36041             c.remove();
36042         }
36043     },
36044
36045     // private
36046     processEvent : function(name, e){
36047         // does this fire select???
36048         Roo.log('grid:processEvent '  + name);
36049         
36050         if (name != 'touchstart' ) {
36051             this.fireEvent(name, e);    
36052         }
36053         
36054         var t = e.getTarget();
36055         var v = this.view;
36056         var header = v.findHeaderIndex(t);
36057         if(header !== false){
36058             var ename = name == 'touchstart' ? 'click' : name;
36059              
36060             this.fireEvent("header" + ename, this, header, e);
36061         }else{
36062             var row = v.findRowIndex(t);
36063             var cell = v.findCellIndex(t);
36064             if (name == 'touchstart') {
36065                 // first touch is always a click.
36066                 // hopefull this happens after selection is updated.?
36067                 name = false;
36068                 
36069                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36070                     var cs = this.selModel.getSelectedCell();
36071                     if (row == cs[0] && cell == cs[1]){
36072                         name = 'dblclick';
36073                     }
36074                 }
36075                 if (typeof(this.selModel.getSelections) != 'undefined') {
36076                     var cs = this.selModel.getSelections();
36077                     var ds = this.dataSource;
36078                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36079                         name = 'dblclick';
36080                     }
36081                 }
36082                 if (!name) {
36083                     return;
36084                 }
36085             }
36086             
36087             
36088             if(row !== false){
36089                 this.fireEvent("row" + name, this, row, e);
36090                 if(cell !== false){
36091                     this.fireEvent("cell" + name, this, row, cell, e);
36092                 }
36093             }
36094         }
36095     },
36096
36097     // private
36098     onClick : function(e){
36099         this.processEvent("click", e);
36100     },
36101    // private
36102     onTouchStart : function(e){
36103         this.processEvent("touchstart", e);
36104     },
36105
36106     // private
36107     onContextMenu : function(e, t){
36108         this.processEvent("contextmenu", e);
36109     },
36110
36111     // private
36112     onDblClick : function(e){
36113         this.processEvent("dblclick", e);
36114     },
36115
36116     // private
36117     walkCells : function(row, col, step, fn, scope){
36118         var cm = this.colModel, clen = cm.getColumnCount();
36119         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36120         if(step < 0){
36121             if(col < 0){
36122                 row--;
36123                 first = false;
36124             }
36125             while(row >= 0){
36126                 if(!first){
36127                     col = clen-1;
36128                 }
36129                 first = false;
36130                 while(col >= 0){
36131                     if(fn.call(scope || this, row, col, cm) === true){
36132                         return [row, col];
36133                     }
36134                     col--;
36135                 }
36136                 row--;
36137             }
36138         } else {
36139             if(col >= clen){
36140                 row++;
36141                 first = false;
36142             }
36143             while(row < rlen){
36144                 if(!first){
36145                     col = 0;
36146                 }
36147                 first = false;
36148                 while(col < clen){
36149                     if(fn.call(scope || this, row, col, cm) === true){
36150                         return [row, col];
36151                     }
36152                     col++;
36153                 }
36154                 row++;
36155             }
36156         }
36157         return null;
36158     },
36159
36160     // private
36161     getSelections : function(){
36162         return this.selModel.getSelections();
36163     },
36164
36165     /**
36166      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36167      * but if manual update is required this method will initiate it.
36168      */
36169     autoSize : function(){
36170         if(this.rendered){
36171             this.view.layout();
36172             if(this.view.adjustForScroll){
36173                 this.view.adjustForScroll();
36174             }
36175         }
36176     },
36177
36178     /**
36179      * Returns the grid's underlying element.
36180      * @return {Element} The element
36181      */
36182     getGridEl : function(){
36183         return this.container;
36184     },
36185
36186     // private for compatibility, overridden by editor grid
36187     stopEditing : function(){},
36188
36189     /**
36190      * Returns the grid's SelectionModel.
36191      * @return {SelectionModel}
36192      */
36193     getSelectionModel : function(){
36194         if(!this.selModel){
36195             this.selModel = new Roo.grid.RowSelectionModel();
36196         }
36197         return this.selModel;
36198     },
36199
36200     /**
36201      * Returns the grid's DataSource.
36202      * @return {DataSource}
36203      */
36204     getDataSource : function(){
36205         return this.dataSource;
36206     },
36207
36208     /**
36209      * Returns the grid's ColumnModel.
36210      * @return {ColumnModel}
36211      */
36212     getColumnModel : function(){
36213         return this.colModel;
36214     },
36215
36216     /**
36217      * Returns the grid's GridView object.
36218      * @return {GridView}
36219      */
36220     getView : function(){
36221         if(!this.view){
36222             this.view = new Roo.grid.GridView(this.viewConfig);
36223         }
36224         return this.view;
36225     },
36226     /**
36227      * Called to get grid's drag proxy text, by default returns this.ddText.
36228      * @return {String}
36229      */
36230     getDragDropText : function(){
36231         var count = this.selModel.getCount();
36232         return String.format(this.ddText, count, count == 1 ? '' : 's');
36233     }
36234 });
36235 /**
36236  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36237  * %0 is replaced with the number of selected rows.
36238  * @type String
36239  */
36240 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36241  * Based on:
36242  * Ext JS Library 1.1.1
36243  * Copyright(c) 2006-2007, Ext JS, LLC.
36244  *
36245  * Originally Released Under LGPL - original licence link has changed is not relivant.
36246  *
36247  * Fork - LGPL
36248  * <script type="text/javascript">
36249  */
36250  
36251 Roo.grid.AbstractGridView = function(){
36252         this.grid = null;
36253         
36254         this.events = {
36255             "beforerowremoved" : true,
36256             "beforerowsinserted" : true,
36257             "beforerefresh" : true,
36258             "rowremoved" : true,
36259             "rowsinserted" : true,
36260             "rowupdated" : true,
36261             "refresh" : true
36262         };
36263     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36264 };
36265
36266 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36267     rowClass : "x-grid-row",
36268     cellClass : "x-grid-cell",
36269     tdClass : "x-grid-td",
36270     hdClass : "x-grid-hd",
36271     splitClass : "x-grid-hd-split",
36272     
36273     init: function(grid){
36274         this.grid = grid;
36275                 var cid = this.grid.getGridEl().id;
36276         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36277         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36278         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36279         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36280         },
36281         
36282     getColumnRenderers : function(){
36283         var renderers = [];
36284         var cm = this.grid.colModel;
36285         var colCount = cm.getColumnCount();
36286         for(var i = 0; i < colCount; i++){
36287             renderers[i] = cm.getRenderer(i);
36288         }
36289         return renderers;
36290     },
36291     
36292     getColumnIds : function(){
36293         var ids = [];
36294         var cm = this.grid.colModel;
36295         var colCount = cm.getColumnCount();
36296         for(var i = 0; i < colCount; i++){
36297             ids[i] = cm.getColumnId(i);
36298         }
36299         return ids;
36300     },
36301     
36302     getDataIndexes : function(){
36303         if(!this.indexMap){
36304             this.indexMap = this.buildIndexMap();
36305         }
36306         return this.indexMap.colToData;
36307     },
36308     
36309     getColumnIndexByDataIndex : function(dataIndex){
36310         if(!this.indexMap){
36311             this.indexMap = this.buildIndexMap();
36312         }
36313         return this.indexMap.dataToCol[dataIndex];
36314     },
36315     
36316     /**
36317      * Set a css style for a column dynamically. 
36318      * @param {Number} colIndex The index of the column
36319      * @param {String} name The css property name
36320      * @param {String} value The css value
36321      */
36322     setCSSStyle : function(colIndex, name, value){
36323         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36324         Roo.util.CSS.updateRule(selector, name, value);
36325     },
36326     
36327     generateRules : function(cm){
36328         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36329         Roo.util.CSS.removeStyleSheet(rulesId);
36330         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36331             var cid = cm.getColumnId(i);
36332             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36333                          this.tdSelector, cid, " {\n}\n",
36334                          this.hdSelector, cid, " {\n}\n",
36335                          this.splitSelector, cid, " {\n}\n");
36336         }
36337         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36338     }
36339 });/*
36340  * Based on:
36341  * Ext JS Library 1.1.1
36342  * Copyright(c) 2006-2007, Ext JS, LLC.
36343  *
36344  * Originally Released Under LGPL - original licence link has changed is not relivant.
36345  *
36346  * Fork - LGPL
36347  * <script type="text/javascript">
36348  */
36349
36350 // private
36351 // This is a support class used internally by the Grid components
36352 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36353     this.grid = grid;
36354     this.view = grid.getView();
36355     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36356     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36357     if(hd2){
36358         this.setHandleElId(Roo.id(hd));
36359         this.setOuterHandleElId(Roo.id(hd2));
36360     }
36361     this.scroll = false;
36362 };
36363 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36364     maxDragWidth: 120,
36365     getDragData : function(e){
36366         var t = Roo.lib.Event.getTarget(e);
36367         var h = this.view.findHeaderCell(t);
36368         if(h){
36369             return {ddel: h.firstChild, header:h};
36370         }
36371         return false;
36372     },
36373
36374     onInitDrag : function(e){
36375         this.view.headersDisabled = true;
36376         var clone = this.dragData.ddel.cloneNode(true);
36377         clone.id = Roo.id();
36378         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36379         this.proxy.update(clone);
36380         return true;
36381     },
36382
36383     afterValidDrop : function(){
36384         var v = this.view;
36385         setTimeout(function(){
36386             v.headersDisabled = false;
36387         }, 50);
36388     },
36389
36390     afterInvalidDrop : function(){
36391         var v = this.view;
36392         setTimeout(function(){
36393             v.headersDisabled = false;
36394         }, 50);
36395     }
36396 });
36397 /*
36398  * Based on:
36399  * Ext JS Library 1.1.1
36400  * Copyright(c) 2006-2007, Ext JS, LLC.
36401  *
36402  * Originally Released Under LGPL - original licence link has changed is not relivant.
36403  *
36404  * Fork - LGPL
36405  * <script type="text/javascript">
36406  */
36407 // private
36408 // This is a support class used internally by the Grid components
36409 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36410     this.grid = grid;
36411     this.view = grid.getView();
36412     // split the proxies so they don't interfere with mouse events
36413     this.proxyTop = Roo.DomHelper.append(document.body, {
36414         cls:"col-move-top", html:"&#160;"
36415     }, true);
36416     this.proxyBottom = Roo.DomHelper.append(document.body, {
36417         cls:"col-move-bottom", html:"&#160;"
36418     }, true);
36419     this.proxyTop.hide = this.proxyBottom.hide = function(){
36420         this.setLeftTop(-100,-100);
36421         this.setStyle("visibility", "hidden");
36422     };
36423     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36424     // temporarily disabled
36425     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36426     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36427 };
36428 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36429     proxyOffsets : [-4, -9],
36430     fly: Roo.Element.fly,
36431
36432     getTargetFromEvent : function(e){
36433         var t = Roo.lib.Event.getTarget(e);
36434         var cindex = this.view.findCellIndex(t);
36435         if(cindex !== false){
36436             return this.view.getHeaderCell(cindex);
36437         }
36438         return null;
36439     },
36440
36441     nextVisible : function(h){
36442         var v = this.view, cm = this.grid.colModel;
36443         h = h.nextSibling;
36444         while(h){
36445             if(!cm.isHidden(v.getCellIndex(h))){
36446                 return h;
36447             }
36448             h = h.nextSibling;
36449         }
36450         return null;
36451     },
36452
36453     prevVisible : function(h){
36454         var v = this.view, cm = this.grid.colModel;
36455         h = h.prevSibling;
36456         while(h){
36457             if(!cm.isHidden(v.getCellIndex(h))){
36458                 return h;
36459             }
36460             h = h.prevSibling;
36461         }
36462         return null;
36463     },
36464
36465     positionIndicator : function(h, n, e){
36466         var x = Roo.lib.Event.getPageX(e);
36467         var r = Roo.lib.Dom.getRegion(n.firstChild);
36468         var px, pt, py = r.top + this.proxyOffsets[1];
36469         if((r.right - x) <= (r.right-r.left)/2){
36470             px = r.right+this.view.borderWidth;
36471             pt = "after";
36472         }else{
36473             px = r.left;
36474             pt = "before";
36475         }
36476         var oldIndex = this.view.getCellIndex(h);
36477         var newIndex = this.view.getCellIndex(n);
36478
36479         if(this.grid.colModel.isFixed(newIndex)){
36480             return false;
36481         }
36482
36483         var locked = this.grid.colModel.isLocked(newIndex);
36484
36485         if(pt == "after"){
36486             newIndex++;
36487         }
36488         if(oldIndex < newIndex){
36489             newIndex--;
36490         }
36491         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36492             return false;
36493         }
36494         px +=  this.proxyOffsets[0];
36495         this.proxyTop.setLeftTop(px, py);
36496         this.proxyTop.show();
36497         if(!this.bottomOffset){
36498             this.bottomOffset = this.view.mainHd.getHeight();
36499         }
36500         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36501         this.proxyBottom.show();
36502         return pt;
36503     },
36504
36505     onNodeEnter : function(n, dd, e, data){
36506         if(data.header != n){
36507             this.positionIndicator(data.header, n, e);
36508         }
36509     },
36510
36511     onNodeOver : function(n, dd, e, data){
36512         var result = false;
36513         if(data.header != n){
36514             result = this.positionIndicator(data.header, n, e);
36515         }
36516         if(!result){
36517             this.proxyTop.hide();
36518             this.proxyBottom.hide();
36519         }
36520         return result ? this.dropAllowed : this.dropNotAllowed;
36521     },
36522
36523     onNodeOut : function(n, dd, e, data){
36524         this.proxyTop.hide();
36525         this.proxyBottom.hide();
36526     },
36527
36528     onNodeDrop : function(n, dd, e, data){
36529         var h = data.header;
36530         if(h != n){
36531             var cm = this.grid.colModel;
36532             var x = Roo.lib.Event.getPageX(e);
36533             var r = Roo.lib.Dom.getRegion(n.firstChild);
36534             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36535             var oldIndex = this.view.getCellIndex(h);
36536             var newIndex = this.view.getCellIndex(n);
36537             var locked = cm.isLocked(newIndex);
36538             if(pt == "after"){
36539                 newIndex++;
36540             }
36541             if(oldIndex < newIndex){
36542                 newIndex--;
36543             }
36544             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36545                 return false;
36546             }
36547             cm.setLocked(oldIndex, locked, true);
36548             cm.moveColumn(oldIndex, newIndex);
36549             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36550             return true;
36551         }
36552         return false;
36553     }
36554 });
36555 /*
36556  * Based on:
36557  * Ext JS Library 1.1.1
36558  * Copyright(c) 2006-2007, Ext JS, LLC.
36559  *
36560  * Originally Released Under LGPL - original licence link has changed is not relivant.
36561  *
36562  * Fork - LGPL
36563  * <script type="text/javascript">
36564  */
36565   
36566 /**
36567  * @class Roo.grid.GridView
36568  * @extends Roo.util.Observable
36569  *
36570  * @constructor
36571  * @param {Object} config
36572  */
36573 Roo.grid.GridView = function(config){
36574     Roo.grid.GridView.superclass.constructor.call(this);
36575     this.el = null;
36576
36577     Roo.apply(this, config);
36578 };
36579
36580 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36581
36582     unselectable :  'unselectable="on"',
36583     unselectableCls :  'x-unselectable',
36584     
36585     
36586     rowClass : "x-grid-row",
36587
36588     cellClass : "x-grid-col",
36589
36590     tdClass : "x-grid-td",
36591
36592     hdClass : "x-grid-hd",
36593
36594     splitClass : "x-grid-split",
36595
36596     sortClasses : ["sort-asc", "sort-desc"],
36597
36598     enableMoveAnim : false,
36599
36600     hlColor: "C3DAF9",
36601
36602     dh : Roo.DomHelper,
36603
36604     fly : Roo.Element.fly,
36605
36606     css : Roo.util.CSS,
36607
36608     borderWidth: 1,
36609
36610     splitOffset: 3,
36611
36612     scrollIncrement : 22,
36613
36614     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36615
36616     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36617
36618     bind : function(ds, cm){
36619         if(this.ds){
36620             this.ds.un("load", this.onLoad, this);
36621             this.ds.un("datachanged", this.onDataChange, this);
36622             this.ds.un("add", this.onAdd, this);
36623             this.ds.un("remove", this.onRemove, this);
36624             this.ds.un("update", this.onUpdate, this);
36625             this.ds.un("clear", this.onClear, this);
36626         }
36627         if(ds){
36628             ds.on("load", this.onLoad, this);
36629             ds.on("datachanged", this.onDataChange, this);
36630             ds.on("add", this.onAdd, this);
36631             ds.on("remove", this.onRemove, this);
36632             ds.on("update", this.onUpdate, this);
36633             ds.on("clear", this.onClear, this);
36634         }
36635         this.ds = ds;
36636
36637         if(this.cm){
36638             this.cm.un("widthchange", this.onColWidthChange, this);
36639             this.cm.un("headerchange", this.onHeaderChange, this);
36640             this.cm.un("hiddenchange", this.onHiddenChange, this);
36641             this.cm.un("columnmoved", this.onColumnMove, this);
36642             this.cm.un("columnlockchange", this.onColumnLock, this);
36643         }
36644         if(cm){
36645             this.generateRules(cm);
36646             cm.on("widthchange", this.onColWidthChange, this);
36647             cm.on("headerchange", this.onHeaderChange, this);
36648             cm.on("hiddenchange", this.onHiddenChange, this);
36649             cm.on("columnmoved", this.onColumnMove, this);
36650             cm.on("columnlockchange", this.onColumnLock, this);
36651         }
36652         this.cm = cm;
36653     },
36654
36655     init: function(grid){
36656         Roo.grid.GridView.superclass.init.call(this, grid);
36657
36658         this.bind(grid.dataSource, grid.colModel);
36659
36660         grid.on("headerclick", this.handleHeaderClick, this);
36661
36662         if(grid.trackMouseOver){
36663             grid.on("mouseover", this.onRowOver, this);
36664             grid.on("mouseout", this.onRowOut, this);
36665         }
36666         grid.cancelTextSelection = function(){};
36667         this.gridId = grid.id;
36668
36669         var tpls = this.templates || {};
36670
36671         if(!tpls.master){
36672             tpls.master = new Roo.Template(
36673                '<div class="x-grid" hidefocus="true">',
36674                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36675                   '<div class="x-grid-topbar"></div>',
36676                   '<div class="x-grid-scroller"><div></div></div>',
36677                   '<div class="x-grid-locked">',
36678                       '<div class="x-grid-header">{lockedHeader}</div>',
36679                       '<div class="x-grid-body">{lockedBody}</div>',
36680                   "</div>",
36681                   '<div class="x-grid-viewport">',
36682                       '<div class="x-grid-header">{header}</div>',
36683                       '<div class="x-grid-body">{body}</div>',
36684                   "</div>",
36685                   '<div class="x-grid-bottombar"></div>',
36686                  
36687                   '<div class="x-grid-resize-proxy">&#160;</div>',
36688                "</div>"
36689             );
36690             tpls.master.disableformats = true;
36691         }
36692
36693         if(!tpls.header){
36694             tpls.header = new Roo.Template(
36695                '<table border="0" cellspacing="0" cellpadding="0">',
36696                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36697                "</table>{splits}"
36698             );
36699             tpls.header.disableformats = true;
36700         }
36701         tpls.header.compile();
36702
36703         if(!tpls.hcell){
36704             tpls.hcell = new Roo.Template(
36705                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36706                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36707                 "</div></td>"
36708              );
36709              tpls.hcell.disableFormats = true;
36710         }
36711         tpls.hcell.compile();
36712
36713         if(!tpls.hsplit){
36714             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36715                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36716             tpls.hsplit.disableFormats = true;
36717         }
36718         tpls.hsplit.compile();
36719
36720         if(!tpls.body){
36721             tpls.body = new Roo.Template(
36722                '<table border="0" cellspacing="0" cellpadding="0">',
36723                "<tbody>{rows}</tbody>",
36724                "</table>"
36725             );
36726             tpls.body.disableFormats = true;
36727         }
36728         tpls.body.compile();
36729
36730         if(!tpls.row){
36731             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36732             tpls.row.disableFormats = true;
36733         }
36734         tpls.row.compile();
36735
36736         if(!tpls.cell){
36737             tpls.cell = new Roo.Template(
36738                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36739                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36740                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36741                 "</td>"
36742             );
36743             tpls.cell.disableFormats = true;
36744         }
36745         tpls.cell.compile();
36746
36747         this.templates = tpls;
36748     },
36749
36750     // remap these for backwards compat
36751     onColWidthChange : function(){
36752         this.updateColumns.apply(this, arguments);
36753     },
36754     onHeaderChange : function(){
36755         this.updateHeaders.apply(this, arguments);
36756     }, 
36757     onHiddenChange : function(){
36758         this.handleHiddenChange.apply(this, arguments);
36759     },
36760     onColumnMove : function(){
36761         this.handleColumnMove.apply(this, arguments);
36762     },
36763     onColumnLock : function(){
36764         this.handleLockChange.apply(this, arguments);
36765     },
36766
36767     onDataChange : function(){
36768         this.refresh();
36769         this.updateHeaderSortState();
36770     },
36771
36772     onClear : function(){
36773         this.refresh();
36774     },
36775
36776     onUpdate : function(ds, record){
36777         this.refreshRow(record);
36778     },
36779
36780     refreshRow : function(record){
36781         var ds = this.ds, index;
36782         if(typeof record == 'number'){
36783             index = record;
36784             record = ds.getAt(index);
36785         }else{
36786             index = ds.indexOf(record);
36787         }
36788         this.insertRows(ds, index, index, true);
36789         this.onRemove(ds, record, index+1, true);
36790         this.syncRowHeights(index, index);
36791         this.layout();
36792         this.fireEvent("rowupdated", this, index, record);
36793     },
36794
36795     onAdd : function(ds, records, index){
36796         this.insertRows(ds, index, index + (records.length-1));
36797     },
36798
36799     onRemove : function(ds, record, index, isUpdate){
36800         if(isUpdate !== true){
36801             this.fireEvent("beforerowremoved", this, index, record);
36802         }
36803         var bt = this.getBodyTable(), lt = this.getLockedTable();
36804         if(bt.rows[index]){
36805             bt.firstChild.removeChild(bt.rows[index]);
36806         }
36807         if(lt.rows[index]){
36808             lt.firstChild.removeChild(lt.rows[index]);
36809         }
36810         if(isUpdate !== true){
36811             this.stripeRows(index);
36812             this.syncRowHeights(index, index);
36813             this.layout();
36814             this.fireEvent("rowremoved", this, index, record);
36815         }
36816     },
36817
36818     onLoad : function(){
36819         this.scrollToTop();
36820     },
36821
36822     /**
36823      * Scrolls the grid to the top
36824      */
36825     scrollToTop : function(){
36826         if(this.scroller){
36827             this.scroller.dom.scrollTop = 0;
36828             this.syncScroll();
36829         }
36830     },
36831
36832     /**
36833      * Gets a panel in the header of the grid that can be used for toolbars etc.
36834      * After modifying the contents of this panel a call to grid.autoSize() may be
36835      * required to register any changes in size.
36836      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36837      * @return Roo.Element
36838      */
36839     getHeaderPanel : function(doShow){
36840         if(doShow){
36841             this.headerPanel.show();
36842         }
36843         return this.headerPanel;
36844     },
36845
36846     /**
36847      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36848      * After modifying the contents of this panel a call to grid.autoSize() may be
36849      * required to register any changes in size.
36850      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36851      * @return Roo.Element
36852      */
36853     getFooterPanel : function(doShow){
36854         if(doShow){
36855             this.footerPanel.show();
36856         }
36857         return this.footerPanel;
36858     },
36859
36860     initElements : function(){
36861         var E = Roo.Element;
36862         var el = this.grid.getGridEl().dom.firstChild;
36863         var cs = el.childNodes;
36864
36865         this.el = new E(el);
36866         
36867          this.focusEl = new E(el.firstChild);
36868         this.focusEl.swallowEvent("click", true);
36869         
36870         this.headerPanel = new E(cs[1]);
36871         this.headerPanel.enableDisplayMode("block");
36872
36873         this.scroller = new E(cs[2]);
36874         this.scrollSizer = new E(this.scroller.dom.firstChild);
36875
36876         this.lockedWrap = new E(cs[3]);
36877         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36878         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36879
36880         this.mainWrap = new E(cs[4]);
36881         this.mainHd = new E(this.mainWrap.dom.firstChild);
36882         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36883
36884         this.footerPanel = new E(cs[5]);
36885         this.footerPanel.enableDisplayMode("block");
36886
36887         this.resizeProxy = new E(cs[6]);
36888
36889         this.headerSelector = String.format(
36890            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36891            this.lockedHd.id, this.mainHd.id
36892         );
36893
36894         this.splitterSelector = String.format(
36895            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36896            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36897         );
36898     },
36899     idToCssName : function(s)
36900     {
36901         return s.replace(/[^a-z0-9]+/ig, '-');
36902     },
36903
36904     getHeaderCell : function(index){
36905         return Roo.DomQuery.select(this.headerSelector)[index];
36906     },
36907
36908     getHeaderCellMeasure : function(index){
36909         return this.getHeaderCell(index).firstChild;
36910     },
36911
36912     getHeaderCellText : function(index){
36913         return this.getHeaderCell(index).firstChild.firstChild;
36914     },
36915
36916     getLockedTable : function(){
36917         return this.lockedBody.dom.firstChild;
36918     },
36919
36920     getBodyTable : function(){
36921         return this.mainBody.dom.firstChild;
36922     },
36923
36924     getLockedRow : function(index){
36925         return this.getLockedTable().rows[index];
36926     },
36927
36928     getRow : function(index){
36929         return this.getBodyTable().rows[index];
36930     },
36931
36932     getRowComposite : function(index){
36933         if(!this.rowEl){
36934             this.rowEl = new Roo.CompositeElementLite();
36935         }
36936         var els = [], lrow, mrow;
36937         if(lrow = this.getLockedRow(index)){
36938             els.push(lrow);
36939         }
36940         if(mrow = this.getRow(index)){
36941             els.push(mrow);
36942         }
36943         this.rowEl.elements = els;
36944         return this.rowEl;
36945     },
36946     /**
36947      * Gets the 'td' of the cell
36948      * 
36949      * @param {Integer} rowIndex row to select
36950      * @param {Integer} colIndex column to select
36951      * 
36952      * @return {Object} 
36953      */
36954     getCell : function(rowIndex, colIndex){
36955         var locked = this.cm.getLockedCount();
36956         var source;
36957         if(colIndex < locked){
36958             source = this.lockedBody.dom.firstChild;
36959         }else{
36960             source = this.mainBody.dom.firstChild;
36961             colIndex -= locked;
36962         }
36963         return source.rows[rowIndex].childNodes[colIndex];
36964     },
36965
36966     getCellText : function(rowIndex, colIndex){
36967         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36968     },
36969
36970     getCellBox : function(cell){
36971         var b = this.fly(cell).getBox();
36972         if(Roo.isOpera){ // opera fails to report the Y
36973             b.y = cell.offsetTop + this.mainBody.getY();
36974         }
36975         return b;
36976     },
36977
36978     getCellIndex : function(cell){
36979         var id = String(cell.className).match(this.cellRE);
36980         if(id){
36981             return parseInt(id[1], 10);
36982         }
36983         return 0;
36984     },
36985
36986     findHeaderIndex : function(n){
36987         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36988         return r ? this.getCellIndex(r) : false;
36989     },
36990
36991     findHeaderCell : function(n){
36992         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36993         return r ? r : false;
36994     },
36995
36996     findRowIndex : function(n){
36997         if(!n){
36998             return false;
36999         }
37000         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37001         return r ? r.rowIndex : false;
37002     },
37003
37004     findCellIndex : function(node){
37005         var stop = this.el.dom;
37006         while(node && node != stop){
37007             if(this.findRE.test(node.className)){
37008                 return this.getCellIndex(node);
37009             }
37010             node = node.parentNode;
37011         }
37012         return false;
37013     },
37014
37015     getColumnId : function(index){
37016         return this.cm.getColumnId(index);
37017     },
37018
37019     getSplitters : function()
37020     {
37021         if(this.splitterSelector){
37022            return Roo.DomQuery.select(this.splitterSelector);
37023         }else{
37024             return null;
37025       }
37026     },
37027
37028     getSplitter : function(index){
37029         return this.getSplitters()[index];
37030     },
37031
37032     onRowOver : function(e, t){
37033         var row;
37034         if((row = this.findRowIndex(t)) !== false){
37035             this.getRowComposite(row).addClass("x-grid-row-over");
37036         }
37037     },
37038
37039     onRowOut : function(e, t){
37040         var row;
37041         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37042             this.getRowComposite(row).removeClass("x-grid-row-over");
37043         }
37044     },
37045
37046     renderHeaders : function(){
37047         var cm = this.cm;
37048         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37049         var cb = [], lb = [], sb = [], lsb = [], p = {};
37050         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37051             p.cellId = "x-grid-hd-0-" + i;
37052             p.splitId = "x-grid-csplit-0-" + i;
37053             p.id = cm.getColumnId(i);
37054             p.title = cm.getColumnTooltip(i) || "";
37055             p.value = cm.getColumnHeader(i) || "";
37056             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37057             if(!cm.isLocked(i)){
37058                 cb[cb.length] = ct.apply(p);
37059                 sb[sb.length] = st.apply(p);
37060             }else{
37061                 lb[lb.length] = ct.apply(p);
37062                 lsb[lsb.length] = st.apply(p);
37063             }
37064         }
37065         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37066                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37067     },
37068
37069     updateHeaders : function(){
37070         var html = this.renderHeaders();
37071         this.lockedHd.update(html[0]);
37072         this.mainHd.update(html[1]);
37073     },
37074
37075     /**
37076      * Focuses the specified row.
37077      * @param {Number} row The row index
37078      */
37079     focusRow : function(row)
37080     {
37081         //Roo.log('GridView.focusRow');
37082         var x = this.scroller.dom.scrollLeft;
37083         this.focusCell(row, 0, false);
37084         this.scroller.dom.scrollLeft = x;
37085     },
37086
37087     /**
37088      * Focuses the specified cell.
37089      * @param {Number} row The row index
37090      * @param {Number} col The column index
37091      * @param {Boolean} hscroll false to disable horizontal scrolling
37092      */
37093     focusCell : function(row, col, hscroll)
37094     {
37095         //Roo.log('GridView.focusCell');
37096         var el = this.ensureVisible(row, col, hscroll);
37097         this.focusEl.alignTo(el, "tl-tl");
37098         if(Roo.isGecko){
37099             this.focusEl.focus();
37100         }else{
37101             this.focusEl.focus.defer(1, this.focusEl);
37102         }
37103     },
37104
37105     /**
37106      * Scrolls the specified cell into view
37107      * @param {Number} row The row index
37108      * @param {Number} col The column index
37109      * @param {Boolean} hscroll false to disable horizontal scrolling
37110      */
37111     ensureVisible : function(row, col, hscroll)
37112     {
37113         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37114         //return null; //disable for testing.
37115         if(typeof row != "number"){
37116             row = row.rowIndex;
37117         }
37118         if(row < 0 && row >= this.ds.getCount()){
37119             return  null;
37120         }
37121         col = (col !== undefined ? col : 0);
37122         var cm = this.grid.colModel;
37123         while(cm.isHidden(col)){
37124             col++;
37125         }
37126
37127         var el = this.getCell(row, col);
37128         if(!el){
37129             return null;
37130         }
37131         var c = this.scroller.dom;
37132
37133         var ctop = parseInt(el.offsetTop, 10);
37134         var cleft = parseInt(el.offsetLeft, 10);
37135         var cbot = ctop + el.offsetHeight;
37136         var cright = cleft + el.offsetWidth;
37137         
37138         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37139         var stop = parseInt(c.scrollTop, 10);
37140         var sleft = parseInt(c.scrollLeft, 10);
37141         var sbot = stop + ch;
37142         var sright = sleft + c.clientWidth;
37143         /*
37144         Roo.log('GridView.ensureVisible:' +
37145                 ' ctop:' + ctop +
37146                 ' c.clientHeight:' + c.clientHeight +
37147                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37148                 ' stop:' + stop +
37149                 ' cbot:' + cbot +
37150                 ' sbot:' + sbot +
37151                 ' ch:' + ch  
37152                 );
37153         */
37154         if(ctop < stop){
37155              c.scrollTop = ctop;
37156             //Roo.log("set scrolltop to ctop DISABLE?");
37157         }else if(cbot > sbot){
37158             //Roo.log("set scrolltop to cbot-ch");
37159             c.scrollTop = cbot-ch;
37160         }
37161         
37162         if(hscroll !== false){
37163             if(cleft < sleft){
37164                 c.scrollLeft = cleft;
37165             }else if(cright > sright){
37166                 c.scrollLeft = cright-c.clientWidth;
37167             }
37168         }
37169          
37170         return el;
37171     },
37172
37173     updateColumns : function(){
37174         this.grid.stopEditing();
37175         var cm = this.grid.colModel, colIds = this.getColumnIds();
37176         //var totalWidth = cm.getTotalWidth();
37177         var pos = 0;
37178         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37179             //if(cm.isHidden(i)) continue;
37180             var w = cm.getColumnWidth(i);
37181             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37182             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37183         }
37184         this.updateSplitters();
37185     },
37186
37187     generateRules : function(cm){
37188         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37189         Roo.util.CSS.removeStyleSheet(rulesId);
37190         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37191             var cid = cm.getColumnId(i);
37192             var align = '';
37193             if(cm.config[i].align){
37194                 align = 'text-align:'+cm.config[i].align+';';
37195             }
37196             var hidden = '';
37197             if(cm.isHidden(i)){
37198                 hidden = 'display:none;';
37199             }
37200             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37201             ruleBuf.push(
37202                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37203                     this.hdSelector, cid, " {\n", align, width, "}\n",
37204                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37205                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37206         }
37207         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37208     },
37209
37210     updateSplitters : function(){
37211         var cm = this.cm, s = this.getSplitters();
37212         if(s){ // splitters not created yet
37213             var pos = 0, locked = true;
37214             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37215                 if(cm.isHidden(i)) continue;
37216                 var w = cm.getColumnWidth(i); // make sure it's a number
37217                 if(!cm.isLocked(i) && locked){
37218                     pos = 0;
37219                     locked = false;
37220                 }
37221                 pos += w;
37222                 s[i].style.left = (pos-this.splitOffset) + "px";
37223             }
37224         }
37225     },
37226
37227     handleHiddenChange : function(colModel, colIndex, hidden){
37228         if(hidden){
37229             this.hideColumn(colIndex);
37230         }else{
37231             this.unhideColumn(colIndex);
37232         }
37233     },
37234
37235     hideColumn : function(colIndex){
37236         var cid = this.getColumnId(colIndex);
37237         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37238         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37239         if(Roo.isSafari){
37240             this.updateHeaders();
37241         }
37242         this.updateSplitters();
37243         this.layout();
37244     },
37245
37246     unhideColumn : function(colIndex){
37247         var cid = this.getColumnId(colIndex);
37248         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37249         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37250
37251         if(Roo.isSafari){
37252             this.updateHeaders();
37253         }
37254         this.updateSplitters();
37255         this.layout();
37256     },
37257
37258     insertRows : function(dm, firstRow, lastRow, isUpdate){
37259         if(firstRow == 0 && lastRow == dm.getCount()-1){
37260             this.refresh();
37261         }else{
37262             if(!isUpdate){
37263                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37264             }
37265             var s = this.getScrollState();
37266             var markup = this.renderRows(firstRow, lastRow);
37267             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37268             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37269             this.restoreScroll(s);
37270             if(!isUpdate){
37271                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37272                 this.syncRowHeights(firstRow, lastRow);
37273                 this.stripeRows(firstRow);
37274                 this.layout();
37275             }
37276         }
37277     },
37278
37279     bufferRows : function(markup, target, index){
37280         var before = null, trows = target.rows, tbody = target.tBodies[0];
37281         if(index < trows.length){
37282             before = trows[index];
37283         }
37284         var b = document.createElement("div");
37285         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37286         var rows = b.firstChild.rows;
37287         for(var i = 0, len = rows.length; i < len; i++){
37288             if(before){
37289                 tbody.insertBefore(rows[0], before);
37290             }else{
37291                 tbody.appendChild(rows[0]);
37292             }
37293         }
37294         b.innerHTML = "";
37295         b = null;
37296     },
37297
37298     deleteRows : function(dm, firstRow, lastRow){
37299         if(dm.getRowCount()<1){
37300             this.fireEvent("beforerefresh", this);
37301             this.mainBody.update("");
37302             this.lockedBody.update("");
37303             this.fireEvent("refresh", this);
37304         }else{
37305             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37306             var bt = this.getBodyTable();
37307             var tbody = bt.firstChild;
37308             var rows = bt.rows;
37309             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37310                 tbody.removeChild(rows[firstRow]);
37311             }
37312             this.stripeRows(firstRow);
37313             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37314         }
37315     },
37316
37317     updateRows : function(dataSource, firstRow, lastRow){
37318         var s = this.getScrollState();
37319         this.refresh();
37320         this.restoreScroll(s);
37321     },
37322
37323     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37324         if(!noRefresh){
37325            this.refresh();
37326         }
37327         this.updateHeaderSortState();
37328     },
37329
37330     getScrollState : function(){
37331         
37332         var sb = this.scroller.dom;
37333         return {left: sb.scrollLeft, top: sb.scrollTop};
37334     },
37335
37336     stripeRows : function(startRow){
37337         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37338             return;
37339         }
37340         startRow = startRow || 0;
37341         var rows = this.getBodyTable().rows;
37342         var lrows = this.getLockedTable().rows;
37343         var cls = ' x-grid-row-alt ';
37344         for(var i = startRow, len = rows.length; i < len; i++){
37345             var row = rows[i], lrow = lrows[i];
37346             var isAlt = ((i+1) % 2 == 0);
37347             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37348             if(isAlt == hasAlt){
37349                 continue;
37350             }
37351             if(isAlt){
37352                 row.className += " x-grid-row-alt";
37353             }else{
37354                 row.className = row.className.replace("x-grid-row-alt", "");
37355             }
37356             if(lrow){
37357                 lrow.className = row.className;
37358             }
37359         }
37360     },
37361
37362     restoreScroll : function(state){
37363         //Roo.log('GridView.restoreScroll');
37364         var sb = this.scroller.dom;
37365         sb.scrollLeft = state.left;
37366         sb.scrollTop = state.top;
37367         this.syncScroll();
37368     },
37369
37370     syncScroll : function(){
37371         //Roo.log('GridView.syncScroll');
37372         var sb = this.scroller.dom;
37373         var sh = this.mainHd.dom;
37374         var bs = this.mainBody.dom;
37375         var lv = this.lockedBody.dom;
37376         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37377         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37378     },
37379
37380     handleScroll : function(e){
37381         this.syncScroll();
37382         var sb = this.scroller.dom;
37383         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37384         e.stopEvent();
37385     },
37386
37387     handleWheel : function(e){
37388         var d = e.getWheelDelta();
37389         this.scroller.dom.scrollTop -= d*22;
37390         // set this here to prevent jumpy scrolling on large tables
37391         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37392         e.stopEvent();
37393     },
37394
37395     renderRows : function(startRow, endRow){
37396         // pull in all the crap needed to render rows
37397         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37398         var colCount = cm.getColumnCount();
37399
37400         if(ds.getCount() < 1){
37401             return ["", ""];
37402         }
37403
37404         // build a map for all the columns
37405         var cs = [];
37406         for(var i = 0; i < colCount; i++){
37407             var name = cm.getDataIndex(i);
37408             cs[i] = {
37409                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37410                 renderer : cm.getRenderer(i),
37411                 id : cm.getColumnId(i),
37412                 locked : cm.isLocked(i)
37413             };
37414         }
37415
37416         startRow = startRow || 0;
37417         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37418
37419         // records to render
37420         var rs = ds.getRange(startRow, endRow);
37421
37422         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37423     },
37424
37425     // As much as I hate to duplicate code, this was branched because FireFox really hates
37426     // [].join("") on strings. The performance difference was substantial enough to
37427     // branch this function
37428     doRender : Roo.isGecko ?
37429             function(cs, rs, ds, startRow, colCount, stripe){
37430                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37431                 // buffers
37432                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37433                 
37434                 var hasListener = this.grid.hasListener('rowclass');
37435                 var rowcfg = {};
37436                 for(var j = 0, len = rs.length; j < len; j++){
37437                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37438                     for(var i = 0; i < colCount; i++){
37439                         c = cs[i];
37440                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37441                         p.id = c.id;
37442                         p.css = p.attr = "";
37443                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37444                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37445                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37446                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37447                         }
37448                         var markup = ct.apply(p);
37449                         if(!c.locked){
37450                             cb+= markup;
37451                         }else{
37452                             lcb+= markup;
37453                         }
37454                     }
37455                     var alt = [];
37456                     if(stripe && ((rowIndex+1) % 2 == 0)){
37457                         alt.push("x-grid-row-alt")
37458                     }
37459                     if(r.dirty){
37460                         alt.push(  " x-grid-dirty-row");
37461                     }
37462                     rp.cells = lcb;
37463                     if(this.getRowClass){
37464                         alt.push(this.getRowClass(r, rowIndex));
37465                     }
37466                     if (hasListener) {
37467                         rowcfg = {
37468                              
37469                             record: r,
37470                             rowIndex : rowIndex,
37471                             rowClass : ''
37472                         }
37473                         this.grid.fireEvent('rowclass', this, rowcfg);
37474                         alt.push(rowcfg.rowClass);
37475                     }
37476                     rp.alt = alt.join(" ");
37477                     lbuf+= rt.apply(rp);
37478                     rp.cells = cb;
37479                     buf+=  rt.apply(rp);
37480                 }
37481                 return [lbuf, buf];
37482             } :
37483             function(cs, rs, ds, startRow, colCount, stripe){
37484                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37485                 // buffers
37486                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37487                 var hasListener = this.grid.hasListener('rowclass');
37488  
37489                 var rowcfg = {};
37490                 for(var j = 0, len = rs.length; j < len; j++){
37491                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37492                     for(var i = 0; i < colCount; i++){
37493                         c = cs[i];
37494                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37495                         p.id = c.id;
37496                         p.css = p.attr = "";
37497                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37498                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37499                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37500                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37501                         }
37502                         
37503                         var markup = ct.apply(p);
37504                         if(!c.locked){
37505                             cb[cb.length] = markup;
37506                         }else{
37507                             lcb[lcb.length] = markup;
37508                         }
37509                     }
37510                     var alt = [];
37511                     if(stripe && ((rowIndex+1) % 2 == 0)){
37512                         alt.push( "x-grid-row-alt");
37513                     }
37514                     if(r.dirty){
37515                         alt.push(" x-grid-dirty-row");
37516                     }
37517                     rp.cells = lcb;
37518                     if(this.getRowClass){
37519                         alt.push( this.getRowClass(r, rowIndex));
37520                     }
37521                     if (hasListener) {
37522                         rowcfg = {
37523                              
37524                             record: r,
37525                             rowIndex : rowIndex,
37526                             rowClass : ''
37527                         }
37528                         this.grid.fireEvent('rowclass', this, rowcfg);
37529                         alt.push(rowcfg.rowClass);
37530                     }
37531                     rp.alt = alt.join(" ");
37532                     rp.cells = lcb.join("");
37533                     lbuf[lbuf.length] = rt.apply(rp);
37534                     rp.cells = cb.join("");
37535                     buf[buf.length] =  rt.apply(rp);
37536                 }
37537                 return [lbuf.join(""), buf.join("")];
37538             },
37539
37540     renderBody : function(){
37541         var markup = this.renderRows();
37542         var bt = this.templates.body;
37543         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37544     },
37545
37546     /**
37547      * Refreshes the grid
37548      * @param {Boolean} headersToo
37549      */
37550     refresh : function(headersToo){
37551         this.fireEvent("beforerefresh", this);
37552         this.grid.stopEditing();
37553         var result = this.renderBody();
37554         this.lockedBody.update(result[0]);
37555         this.mainBody.update(result[1]);
37556         if(headersToo === true){
37557             this.updateHeaders();
37558             this.updateColumns();
37559             this.updateSplitters();
37560             this.updateHeaderSortState();
37561         }
37562         this.syncRowHeights();
37563         this.layout();
37564         this.fireEvent("refresh", this);
37565     },
37566
37567     handleColumnMove : function(cm, oldIndex, newIndex){
37568         this.indexMap = null;
37569         var s = this.getScrollState();
37570         this.refresh(true);
37571         this.restoreScroll(s);
37572         this.afterMove(newIndex);
37573     },
37574
37575     afterMove : function(colIndex){
37576         if(this.enableMoveAnim && Roo.enableFx){
37577             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37578         }
37579         // if multisort - fix sortOrder, and reload..
37580         if (this.grid.dataSource.multiSort) {
37581             // the we can call sort again..
37582             var dm = this.grid.dataSource;
37583             var cm = this.grid.colModel;
37584             var so = [];
37585             for(var i = 0; i < cm.config.length; i++ ) {
37586                 
37587                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37588                     continue; // dont' bother, it's not in sort list or being set.
37589                 }
37590                 
37591                 so.push(cm.config[i].dataIndex);
37592             };
37593             dm.sortOrder = so;
37594             dm.load(dm.lastOptions);
37595             
37596             
37597         }
37598         
37599     },
37600
37601     updateCell : function(dm, rowIndex, dataIndex){
37602         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37603         if(typeof colIndex == "undefined"){ // not present in grid
37604             return;
37605         }
37606         var cm = this.grid.colModel;
37607         var cell = this.getCell(rowIndex, colIndex);
37608         var cellText = this.getCellText(rowIndex, colIndex);
37609
37610         var p = {
37611             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37612             id : cm.getColumnId(colIndex),
37613             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37614         };
37615         var renderer = cm.getRenderer(colIndex);
37616         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37617         if(typeof val == "undefined" || val === "") val = "&#160;";
37618         cellText.innerHTML = val;
37619         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37620         this.syncRowHeights(rowIndex, rowIndex);
37621     },
37622
37623     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37624         var maxWidth = 0;
37625         if(this.grid.autoSizeHeaders){
37626             var h = this.getHeaderCellMeasure(colIndex);
37627             maxWidth = Math.max(maxWidth, h.scrollWidth);
37628         }
37629         var tb, index;
37630         if(this.cm.isLocked(colIndex)){
37631             tb = this.getLockedTable();
37632             index = colIndex;
37633         }else{
37634             tb = this.getBodyTable();
37635             index = colIndex - this.cm.getLockedCount();
37636         }
37637         if(tb && tb.rows){
37638             var rows = tb.rows;
37639             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37640             for(var i = 0; i < stopIndex; i++){
37641                 var cell = rows[i].childNodes[index].firstChild;
37642                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37643             }
37644         }
37645         return maxWidth + /*margin for error in IE*/ 5;
37646     },
37647     /**
37648      * Autofit a column to its content.
37649      * @param {Number} colIndex
37650      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37651      */
37652      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37653          if(this.cm.isHidden(colIndex)){
37654              return; // can't calc a hidden column
37655          }
37656         if(forceMinSize){
37657             var cid = this.cm.getColumnId(colIndex);
37658             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37659            if(this.grid.autoSizeHeaders){
37660                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37661            }
37662         }
37663         var newWidth = this.calcColumnWidth(colIndex);
37664         this.cm.setColumnWidth(colIndex,
37665             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37666         if(!suppressEvent){
37667             this.grid.fireEvent("columnresize", colIndex, newWidth);
37668         }
37669     },
37670
37671     /**
37672      * Autofits all columns to their content and then expands to fit any extra space in the grid
37673      */
37674      autoSizeColumns : function(){
37675         var cm = this.grid.colModel;
37676         var colCount = cm.getColumnCount();
37677         for(var i = 0; i < colCount; i++){
37678             this.autoSizeColumn(i, true, true);
37679         }
37680         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37681             this.fitColumns();
37682         }else{
37683             this.updateColumns();
37684             this.layout();
37685         }
37686     },
37687
37688     /**
37689      * Autofits all columns to the grid's width proportionate with their current size
37690      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37691      */
37692     fitColumns : function(reserveScrollSpace){
37693         var cm = this.grid.colModel;
37694         var colCount = cm.getColumnCount();
37695         var cols = [];
37696         var width = 0;
37697         var i, w;
37698         for (i = 0; i < colCount; i++){
37699             if(!cm.isHidden(i) && !cm.isFixed(i)){
37700                 w = cm.getColumnWidth(i);
37701                 cols.push(i);
37702                 cols.push(w);
37703                 width += w;
37704             }
37705         }
37706         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37707         if(reserveScrollSpace){
37708             avail -= 17;
37709         }
37710         var frac = (avail - cm.getTotalWidth())/width;
37711         while (cols.length){
37712             w = cols.pop();
37713             i = cols.pop();
37714             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37715         }
37716         this.updateColumns();
37717         this.layout();
37718     },
37719
37720     onRowSelect : function(rowIndex){
37721         var row = this.getRowComposite(rowIndex);
37722         row.addClass("x-grid-row-selected");
37723     },
37724
37725     onRowDeselect : function(rowIndex){
37726         var row = this.getRowComposite(rowIndex);
37727         row.removeClass("x-grid-row-selected");
37728     },
37729
37730     onCellSelect : function(row, col){
37731         var cell = this.getCell(row, col);
37732         if(cell){
37733             Roo.fly(cell).addClass("x-grid-cell-selected");
37734         }
37735     },
37736
37737     onCellDeselect : function(row, col){
37738         var cell = this.getCell(row, col);
37739         if(cell){
37740             Roo.fly(cell).removeClass("x-grid-cell-selected");
37741         }
37742     },
37743
37744     updateHeaderSortState : function(){
37745         
37746         // sort state can be single { field: xxx, direction : yyy}
37747         // or   { xxx=>ASC , yyy : DESC ..... }
37748         
37749         var mstate = {};
37750         if (!this.ds.multiSort) { 
37751             var state = this.ds.getSortState();
37752             if(!state){
37753                 return;
37754             }
37755             mstate[state.field] = state.direction;
37756             // FIXME... - this is not used here.. but might be elsewhere..
37757             this.sortState = state;
37758             
37759         } else {
37760             mstate = this.ds.sortToggle;
37761         }
37762         //remove existing sort classes..
37763         
37764         var sc = this.sortClasses;
37765         var hds = this.el.select(this.headerSelector).removeClass(sc);
37766         
37767         for(var f in mstate) {
37768         
37769             var sortColumn = this.cm.findColumnIndex(f);
37770             
37771             if(sortColumn != -1){
37772                 var sortDir = mstate[f];        
37773                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37774             }
37775         }
37776         
37777          
37778         
37779     },
37780
37781
37782     handleHeaderClick : function(g, index,e){
37783         
37784         Roo.log("header click");
37785         
37786         if (Roo.isTouch) {
37787             // touch events on header are handled by context
37788             this.handleHdCtx(g,index,e);
37789             return;
37790         }
37791         
37792         
37793         if(this.headersDisabled){
37794             return;
37795         }
37796         var dm = g.dataSource, cm = g.colModel;
37797         if(!cm.isSortable(index)){
37798             return;
37799         }
37800         g.stopEditing();
37801         
37802         if (dm.multiSort) {
37803             // update the sortOrder
37804             var so = [];
37805             for(var i = 0; i < cm.config.length; i++ ) {
37806                 
37807                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37808                     continue; // dont' bother, it's not in sort list or being set.
37809                 }
37810                 
37811                 so.push(cm.config[i].dataIndex);
37812             };
37813             dm.sortOrder = so;
37814         }
37815         
37816         
37817         dm.sort(cm.getDataIndex(index));
37818     },
37819
37820
37821     destroy : function(){
37822         if(this.colMenu){
37823             this.colMenu.removeAll();
37824             Roo.menu.MenuMgr.unregister(this.colMenu);
37825             this.colMenu.getEl().remove();
37826             delete this.colMenu;
37827         }
37828         if(this.hmenu){
37829             this.hmenu.removeAll();
37830             Roo.menu.MenuMgr.unregister(this.hmenu);
37831             this.hmenu.getEl().remove();
37832             delete this.hmenu;
37833         }
37834         if(this.grid.enableColumnMove){
37835             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37836             if(dds){
37837                 for(var dd in dds){
37838                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37839                         var elid = dds[dd].dragElId;
37840                         dds[dd].unreg();
37841                         Roo.get(elid).remove();
37842                     } else if(dds[dd].config.isTarget){
37843                         dds[dd].proxyTop.remove();
37844                         dds[dd].proxyBottom.remove();
37845                         dds[dd].unreg();
37846                     }
37847                     if(Roo.dd.DDM.locationCache[dd]){
37848                         delete Roo.dd.DDM.locationCache[dd];
37849                     }
37850                 }
37851                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37852             }
37853         }
37854         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37855         this.bind(null, null);
37856         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37857     },
37858
37859     handleLockChange : function(){
37860         this.refresh(true);
37861     },
37862
37863     onDenyColumnLock : function(){
37864
37865     },
37866
37867     onDenyColumnHide : function(){
37868
37869     },
37870
37871     handleHdMenuClick : function(item){
37872         var index = this.hdCtxIndex;
37873         var cm = this.cm, ds = this.ds;
37874         switch(item.id){
37875             case "asc":
37876                 ds.sort(cm.getDataIndex(index), "ASC");
37877                 break;
37878             case "desc":
37879                 ds.sort(cm.getDataIndex(index), "DESC");
37880                 break;
37881             case "lock":
37882                 var lc = cm.getLockedCount();
37883                 if(cm.getColumnCount(true) <= lc+1){
37884                     this.onDenyColumnLock();
37885                     return;
37886                 }
37887                 if(lc != index){
37888                     cm.setLocked(index, true, true);
37889                     cm.moveColumn(index, lc);
37890                     this.grid.fireEvent("columnmove", index, lc);
37891                 }else{
37892                     cm.setLocked(index, true);
37893                 }
37894             break;
37895             case "unlock":
37896                 var lc = cm.getLockedCount();
37897                 if((lc-1) != index){
37898                     cm.setLocked(index, false, true);
37899                     cm.moveColumn(index, lc-1);
37900                     this.grid.fireEvent("columnmove", index, lc-1);
37901                 }else{
37902                     cm.setLocked(index, false);
37903                 }
37904             break;
37905             case 'wider': // used to expand cols on touch..
37906             case 'narrow':
37907                 var cw = cm.getColumnWidth(index);
37908                 cw += (item.id == 'wider' ? 1 : -1) * 50;
37909                 cw = Math.max(0, cw);
37910                 cw = Math.min(cw,4000);
37911                 cm.setColumnWidth(index, cw);
37912                 break;
37913                 
37914             default:
37915                 index = cm.getIndexById(item.id.substr(4));
37916                 if(index != -1){
37917                     if(item.checked && cm.getColumnCount(true) <= 1){
37918                         this.onDenyColumnHide();
37919                         return false;
37920                     }
37921                     cm.setHidden(index, item.checked);
37922                 }
37923         }
37924         return true;
37925     },
37926
37927     beforeColMenuShow : function(){
37928         var cm = this.cm,  colCount = cm.getColumnCount();
37929         this.colMenu.removeAll();
37930         for(var i = 0; i < colCount; i++){
37931             this.colMenu.add(new Roo.menu.CheckItem({
37932                 id: "col-"+cm.getColumnId(i),
37933                 text: cm.getColumnHeader(i),
37934                 checked: !cm.isHidden(i),
37935                 hideOnClick:false
37936             }));
37937         }
37938     },
37939
37940     handleHdCtx : function(g, index, e){
37941         e.stopEvent();
37942         var hd = this.getHeaderCell(index);
37943         this.hdCtxIndex = index;
37944         var ms = this.hmenu.items, cm = this.cm;
37945         ms.get("asc").setDisabled(!cm.isSortable(index));
37946         ms.get("desc").setDisabled(!cm.isSortable(index));
37947         if(this.grid.enableColLock !== false){
37948             ms.get("lock").setDisabled(cm.isLocked(index));
37949             ms.get("unlock").setDisabled(!cm.isLocked(index));
37950         }
37951         this.hmenu.show(hd, "tl-bl");
37952     },
37953
37954     handleHdOver : function(e){
37955         var hd = this.findHeaderCell(e.getTarget());
37956         if(hd && !this.headersDisabled){
37957             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37958                this.fly(hd).addClass("x-grid-hd-over");
37959             }
37960         }
37961     },
37962
37963     handleHdOut : function(e){
37964         var hd = this.findHeaderCell(e.getTarget());
37965         if(hd){
37966             this.fly(hd).removeClass("x-grid-hd-over");
37967         }
37968     },
37969
37970     handleSplitDblClick : function(e, t){
37971         var i = this.getCellIndex(t);
37972         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37973             this.autoSizeColumn(i, true);
37974             this.layout();
37975         }
37976     },
37977
37978     render : function(){
37979
37980         var cm = this.cm;
37981         var colCount = cm.getColumnCount();
37982
37983         if(this.grid.monitorWindowResize === true){
37984             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37985         }
37986         var header = this.renderHeaders();
37987         var body = this.templates.body.apply({rows:""});
37988         var html = this.templates.master.apply({
37989             lockedBody: body,
37990             body: body,
37991             lockedHeader: header[0],
37992             header: header[1]
37993         });
37994
37995         //this.updateColumns();
37996
37997         this.grid.getGridEl().dom.innerHTML = html;
37998
37999         this.initElements();
38000         
38001         // a kludge to fix the random scolling effect in webkit
38002         this.el.on("scroll", function() {
38003             this.el.dom.scrollTop=0; // hopefully not recursive..
38004         },this);
38005
38006         this.scroller.on("scroll", this.handleScroll, this);
38007         this.lockedBody.on("mousewheel", this.handleWheel, this);
38008         this.mainBody.on("mousewheel", this.handleWheel, this);
38009
38010         this.mainHd.on("mouseover", this.handleHdOver, this);
38011         this.mainHd.on("mouseout", this.handleHdOut, this);
38012         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38013                 {delegate: "."+this.splitClass});
38014
38015         this.lockedHd.on("mouseover", this.handleHdOver, this);
38016         this.lockedHd.on("mouseout", this.handleHdOut, this);
38017         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38018                 {delegate: "."+this.splitClass});
38019
38020         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38021             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38022         }
38023
38024         this.updateSplitters();
38025
38026         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38027             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38028             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38029         }
38030
38031         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38032             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38033             this.hmenu.add(
38034                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38035                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38036             );
38037             if(this.grid.enableColLock !== false){
38038                 this.hmenu.add('-',
38039                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38040                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38041                 );
38042             }
38043             if (Roo.isTouch) {
38044                  this.hmenu.add('-',
38045                     {id:"wider", text: this.columnsWiderText},
38046                     {id:"narrow", text: this.columnsNarrowText }
38047                 );
38048                 
38049                  
38050             }
38051             
38052             if(this.grid.enableColumnHide !== false){
38053
38054                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38055                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38056                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38057
38058                 this.hmenu.add('-',
38059                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38060                 );
38061             }
38062             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38063
38064             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38065         }
38066
38067         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38068             this.dd = new Roo.grid.GridDragZone(this.grid, {
38069                 ddGroup : this.grid.ddGroup || 'GridDD'
38070             });
38071             
38072         }
38073
38074         /*
38075         for(var i = 0; i < colCount; i++){
38076             if(cm.isHidden(i)){
38077                 this.hideColumn(i);
38078             }
38079             if(cm.config[i].align){
38080                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38081                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38082             }
38083         }*/
38084         
38085         this.updateHeaderSortState();
38086
38087         this.beforeInitialResize();
38088         this.layout(true);
38089
38090         // two part rendering gives faster view to the user
38091         this.renderPhase2.defer(1, this);
38092     },
38093
38094     renderPhase2 : function(){
38095         // render the rows now
38096         this.refresh();
38097         if(this.grid.autoSizeColumns){
38098             this.autoSizeColumns();
38099         }
38100     },
38101
38102     beforeInitialResize : function(){
38103
38104     },
38105
38106     onColumnSplitterMoved : function(i, w){
38107         this.userResized = true;
38108         var cm = this.grid.colModel;
38109         cm.setColumnWidth(i, w, true);
38110         var cid = cm.getColumnId(i);
38111         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38112         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38113         this.updateSplitters();
38114         this.layout();
38115         this.grid.fireEvent("columnresize", i, w);
38116     },
38117
38118     syncRowHeights : function(startIndex, endIndex){
38119         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38120             startIndex = startIndex || 0;
38121             var mrows = this.getBodyTable().rows;
38122             var lrows = this.getLockedTable().rows;
38123             var len = mrows.length-1;
38124             endIndex = Math.min(endIndex || len, len);
38125             for(var i = startIndex; i <= endIndex; i++){
38126                 var m = mrows[i], l = lrows[i];
38127                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38128                 m.style.height = l.style.height = h + "px";
38129             }
38130         }
38131     },
38132
38133     layout : function(initialRender, is2ndPass){
38134         var g = this.grid;
38135         var auto = g.autoHeight;
38136         var scrollOffset = 16;
38137         var c = g.getGridEl(), cm = this.cm,
38138                 expandCol = g.autoExpandColumn,
38139                 gv = this;
38140         //c.beginMeasure();
38141
38142         if(!c.dom.offsetWidth){ // display:none?
38143             if(initialRender){
38144                 this.lockedWrap.show();
38145                 this.mainWrap.show();
38146             }
38147             return;
38148         }
38149
38150         var hasLock = this.cm.isLocked(0);
38151
38152         var tbh = this.headerPanel.getHeight();
38153         var bbh = this.footerPanel.getHeight();
38154
38155         if(auto){
38156             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38157             var newHeight = ch + c.getBorderWidth("tb");
38158             if(g.maxHeight){
38159                 newHeight = Math.min(g.maxHeight, newHeight);
38160             }
38161             c.setHeight(newHeight);
38162         }
38163
38164         if(g.autoWidth){
38165             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38166         }
38167
38168         var s = this.scroller;
38169
38170         var csize = c.getSize(true);
38171
38172         this.el.setSize(csize.width, csize.height);
38173
38174         this.headerPanel.setWidth(csize.width);
38175         this.footerPanel.setWidth(csize.width);
38176
38177         var hdHeight = this.mainHd.getHeight();
38178         var vw = csize.width;
38179         var vh = csize.height - (tbh + bbh);
38180
38181         s.setSize(vw, vh);
38182
38183         var bt = this.getBodyTable();
38184         var ltWidth = hasLock ?
38185                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38186
38187         var scrollHeight = bt.offsetHeight;
38188         var scrollWidth = ltWidth + bt.offsetWidth;
38189         var vscroll = false, hscroll = false;
38190
38191         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38192
38193         var lw = this.lockedWrap, mw = this.mainWrap;
38194         var lb = this.lockedBody, mb = this.mainBody;
38195
38196         setTimeout(function(){
38197             var t = s.dom.offsetTop;
38198             var w = s.dom.clientWidth,
38199                 h = s.dom.clientHeight;
38200
38201             lw.setTop(t);
38202             lw.setSize(ltWidth, h);
38203
38204             mw.setLeftTop(ltWidth, t);
38205             mw.setSize(w-ltWidth, h);
38206
38207             lb.setHeight(h-hdHeight);
38208             mb.setHeight(h-hdHeight);
38209
38210             if(is2ndPass !== true && !gv.userResized && expandCol){
38211                 // high speed resize without full column calculation
38212                 
38213                 var ci = cm.getIndexById(expandCol);
38214                 if (ci < 0) {
38215                     ci = cm.findColumnIndex(expandCol);
38216                 }
38217                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38218                 var expandId = cm.getColumnId(ci);
38219                 var  tw = cm.getTotalWidth(false);
38220                 var currentWidth = cm.getColumnWidth(ci);
38221                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38222                 if(currentWidth != cw){
38223                     cm.setColumnWidth(ci, cw, true);
38224                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38225                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38226                     gv.updateSplitters();
38227                     gv.layout(false, true);
38228                 }
38229             }
38230
38231             if(initialRender){
38232                 lw.show();
38233                 mw.show();
38234             }
38235             //c.endMeasure();
38236         }, 10);
38237     },
38238
38239     onWindowResize : function(){
38240         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38241             return;
38242         }
38243         this.layout();
38244     },
38245
38246     appendFooter : function(parentEl){
38247         return null;
38248     },
38249
38250     sortAscText : "Sort Ascending",
38251     sortDescText : "Sort Descending",
38252     lockText : "Lock Column",
38253     unlockText : "Unlock Column",
38254     columnsText : "Columns",
38255  
38256     columnsWiderText : "Wider",
38257     columnsNarrowText : "Thinner"
38258 });
38259
38260
38261 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38262     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38263     this.proxy.el.addClass('x-grid3-col-dd');
38264 };
38265
38266 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38267     handleMouseDown : function(e){
38268
38269     },
38270
38271     callHandleMouseDown : function(e){
38272         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38273     }
38274 });
38275 /*
38276  * Based on:
38277  * Ext JS Library 1.1.1
38278  * Copyright(c) 2006-2007, Ext JS, LLC.
38279  *
38280  * Originally Released Under LGPL - original licence link has changed is not relivant.
38281  *
38282  * Fork - LGPL
38283  * <script type="text/javascript">
38284  */
38285  
38286 // private
38287 // This is a support class used internally by the Grid components
38288 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38289     this.grid = grid;
38290     this.view = grid.getView();
38291     this.proxy = this.view.resizeProxy;
38292     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38293         "gridSplitters" + this.grid.getGridEl().id, {
38294         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38295     });
38296     this.setHandleElId(Roo.id(hd));
38297     this.setOuterHandleElId(Roo.id(hd2));
38298     this.scroll = false;
38299 };
38300 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38301     fly: Roo.Element.fly,
38302
38303     b4StartDrag : function(x, y){
38304         this.view.headersDisabled = true;
38305         this.proxy.setHeight(this.view.mainWrap.getHeight());
38306         var w = this.cm.getColumnWidth(this.cellIndex);
38307         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38308         this.resetConstraints();
38309         this.setXConstraint(minw, 1000);
38310         this.setYConstraint(0, 0);
38311         this.minX = x - minw;
38312         this.maxX = x + 1000;
38313         this.startPos = x;
38314         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38315     },
38316
38317
38318     handleMouseDown : function(e){
38319         ev = Roo.EventObject.setEvent(e);
38320         var t = this.fly(ev.getTarget());
38321         if(t.hasClass("x-grid-split")){
38322             this.cellIndex = this.view.getCellIndex(t.dom);
38323             this.split = t.dom;
38324             this.cm = this.grid.colModel;
38325             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38326                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38327             }
38328         }
38329     },
38330
38331     endDrag : function(e){
38332         this.view.headersDisabled = false;
38333         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38334         var diff = endX - this.startPos;
38335         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38336     },
38337
38338     autoOffset : function(){
38339         this.setDelta(0,0);
38340     }
38341 });/*
38342  * Based on:
38343  * Ext JS Library 1.1.1
38344  * Copyright(c) 2006-2007, Ext JS, LLC.
38345  *
38346  * Originally Released Under LGPL - original licence link has changed is not relivant.
38347  *
38348  * Fork - LGPL
38349  * <script type="text/javascript">
38350  */
38351  
38352 // private
38353 // This is a support class used internally by the Grid components
38354 Roo.grid.GridDragZone = function(grid, config){
38355     this.view = grid.getView();
38356     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38357     if(this.view.lockedBody){
38358         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38359         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38360     }
38361     this.scroll = false;
38362     this.grid = grid;
38363     this.ddel = document.createElement('div');
38364     this.ddel.className = 'x-grid-dd-wrap';
38365 };
38366
38367 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38368     ddGroup : "GridDD",
38369
38370     getDragData : function(e){
38371         var t = Roo.lib.Event.getTarget(e);
38372         var rowIndex = this.view.findRowIndex(t);
38373         var sm = this.grid.selModel;
38374             
38375         //Roo.log(rowIndex);
38376         
38377         if (sm.getSelectedCell) {
38378             // cell selection..
38379             if (!sm.getSelectedCell()) {
38380                 return false;
38381             }
38382             if (rowIndex != sm.getSelectedCell()[0]) {
38383                 return false;
38384             }
38385         
38386         }
38387         
38388         if(rowIndex !== false){
38389             
38390             // if editorgrid.. 
38391             
38392             
38393             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38394                
38395             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38396               //  
38397             //}
38398             if (e.hasModifier()){
38399                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38400             }
38401             
38402             Roo.log("getDragData");
38403             
38404             return {
38405                 grid: this.grid,
38406                 ddel: this.ddel,
38407                 rowIndex: rowIndex,
38408                 selections:sm.getSelections ? sm.getSelections() : (
38409                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38410                 )
38411             };
38412         }
38413         return false;
38414     },
38415
38416     onInitDrag : function(e){
38417         var data = this.dragData;
38418         this.ddel.innerHTML = this.grid.getDragDropText();
38419         this.proxy.update(this.ddel);
38420         // fire start drag?
38421     },
38422
38423     afterRepair : function(){
38424         this.dragging = false;
38425     },
38426
38427     getRepairXY : function(e, data){
38428         return false;
38429     },
38430
38431     onEndDrag : function(data, e){
38432         // fire end drag?
38433     },
38434
38435     onValidDrop : function(dd, e, id){
38436         // fire drag drop?
38437         this.hideProxy();
38438     },
38439
38440     beforeInvalidDrop : function(e, id){
38441
38442     }
38443 });/*
38444  * Based on:
38445  * Ext JS Library 1.1.1
38446  * Copyright(c) 2006-2007, Ext JS, LLC.
38447  *
38448  * Originally Released Under LGPL - original licence link has changed is not relivant.
38449  *
38450  * Fork - LGPL
38451  * <script type="text/javascript">
38452  */
38453  
38454
38455 /**
38456  * @class Roo.grid.ColumnModel
38457  * @extends Roo.util.Observable
38458  * This is the default implementation of a ColumnModel used by the Grid. It defines
38459  * the columns in the grid.
38460  * <br>Usage:<br>
38461  <pre><code>
38462  var colModel = new Roo.grid.ColumnModel([
38463         {header: "Ticker", width: 60, sortable: true, locked: true},
38464         {header: "Company Name", width: 150, sortable: true},
38465         {header: "Market Cap.", width: 100, sortable: true},
38466         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38467         {header: "Employees", width: 100, sortable: true, resizable: false}
38468  ]);
38469  </code></pre>
38470  * <p>
38471  
38472  * The config options listed for this class are options which may appear in each
38473  * individual column definition.
38474  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38475  * @constructor
38476  * @param {Object} config An Array of column config objects. See this class's
38477  * config objects for details.
38478 */
38479 Roo.grid.ColumnModel = function(config){
38480         /**
38481      * The config passed into the constructor
38482      */
38483     this.config = config;
38484     this.lookup = {};
38485
38486     // if no id, create one
38487     // if the column does not have a dataIndex mapping,
38488     // map it to the order it is in the config
38489     for(var i = 0, len = config.length; i < len; i++){
38490         var c = config[i];
38491         if(typeof c.dataIndex == "undefined"){
38492             c.dataIndex = i;
38493         }
38494         if(typeof c.renderer == "string"){
38495             c.renderer = Roo.util.Format[c.renderer];
38496         }
38497         if(typeof c.id == "undefined"){
38498             c.id = Roo.id();
38499         }
38500         if(c.editor && c.editor.xtype){
38501             c.editor  = Roo.factory(c.editor, Roo.grid);
38502         }
38503         if(c.editor && c.editor.isFormField){
38504             c.editor = new Roo.grid.GridEditor(c.editor);
38505         }
38506         this.lookup[c.id] = c;
38507     }
38508
38509     /**
38510      * The width of columns which have no width specified (defaults to 100)
38511      * @type Number
38512      */
38513     this.defaultWidth = 100;
38514
38515     /**
38516      * Default sortable of columns which have no sortable specified (defaults to false)
38517      * @type Boolean
38518      */
38519     this.defaultSortable = false;
38520
38521     this.addEvents({
38522         /**
38523              * @event widthchange
38524              * Fires when the width of a column changes.
38525              * @param {ColumnModel} this
38526              * @param {Number} columnIndex The column index
38527              * @param {Number} newWidth The new width
38528              */
38529             "widthchange": true,
38530         /**
38531              * @event headerchange
38532              * Fires when the text of a header changes.
38533              * @param {ColumnModel} this
38534              * @param {Number} columnIndex The column index
38535              * @param {Number} newText The new header text
38536              */
38537             "headerchange": true,
38538         /**
38539              * @event hiddenchange
38540              * Fires when a column is hidden or "unhidden".
38541              * @param {ColumnModel} this
38542              * @param {Number} columnIndex The column index
38543              * @param {Boolean} hidden true if hidden, false otherwise
38544              */
38545             "hiddenchange": true,
38546             /**
38547          * @event columnmoved
38548          * Fires when a column is moved.
38549          * @param {ColumnModel} this
38550          * @param {Number} oldIndex
38551          * @param {Number} newIndex
38552          */
38553         "columnmoved" : true,
38554         /**
38555          * @event columlockchange
38556          * Fires when a column's locked state is changed
38557          * @param {ColumnModel} this
38558          * @param {Number} colIndex
38559          * @param {Boolean} locked true if locked
38560          */
38561         "columnlockchange" : true
38562     });
38563     Roo.grid.ColumnModel.superclass.constructor.call(this);
38564 };
38565 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38566     /**
38567      * @cfg {String} header The header text to display in the Grid view.
38568      */
38569     /**
38570      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38571      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38572      * specified, the column's index is used as an index into the Record's data Array.
38573      */
38574     /**
38575      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38576      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38577      */
38578     /**
38579      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38580      * Defaults to the value of the {@link #defaultSortable} property.
38581      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38582      */
38583     /**
38584      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38585      */
38586     /**
38587      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38588      */
38589     /**
38590      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38591      */
38592     /**
38593      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38594      */
38595     /**
38596      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38597      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38598      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38599      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38600      */
38601        /**
38602      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38603      */
38604     /**
38605      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38606      */
38607     /**
38608      * @cfg {String} cursor (Optional)
38609      */
38610     /**
38611      * Returns the id of the column at the specified index.
38612      * @param {Number} index The column index
38613      * @return {String} the id
38614      */
38615     getColumnId : function(index){
38616         return this.config[index].id;
38617     },
38618
38619     /**
38620      * Returns the column for a specified id.
38621      * @param {String} id The column id
38622      * @return {Object} the column
38623      */
38624     getColumnById : function(id){
38625         return this.lookup[id];
38626     },
38627
38628     
38629     /**
38630      * Returns the column for a specified dataIndex.
38631      * @param {String} dataIndex The column dataIndex
38632      * @return {Object|Boolean} the column or false if not found
38633      */
38634     getColumnByDataIndex: function(dataIndex){
38635         var index = this.findColumnIndex(dataIndex);
38636         return index > -1 ? this.config[index] : false;
38637     },
38638     
38639     /**
38640      * Returns the index for a specified column id.
38641      * @param {String} id The column id
38642      * @return {Number} the index, or -1 if not found
38643      */
38644     getIndexById : function(id){
38645         for(var i = 0, len = this.config.length; i < len; i++){
38646             if(this.config[i].id == id){
38647                 return i;
38648             }
38649         }
38650         return -1;
38651     },
38652     
38653     /**
38654      * Returns the index for a specified column dataIndex.
38655      * @param {String} dataIndex The column dataIndex
38656      * @return {Number} the index, or -1 if not found
38657      */
38658     
38659     findColumnIndex : function(dataIndex){
38660         for(var i = 0, len = this.config.length; i < len; i++){
38661             if(this.config[i].dataIndex == dataIndex){
38662                 return i;
38663             }
38664         }
38665         return -1;
38666     },
38667     
38668     
38669     moveColumn : function(oldIndex, newIndex){
38670         var c = this.config[oldIndex];
38671         this.config.splice(oldIndex, 1);
38672         this.config.splice(newIndex, 0, c);
38673         this.dataMap = null;
38674         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38675     },
38676
38677     isLocked : function(colIndex){
38678         return this.config[colIndex].locked === true;
38679     },
38680
38681     setLocked : function(colIndex, value, suppressEvent){
38682         if(this.isLocked(colIndex) == value){
38683             return;
38684         }
38685         this.config[colIndex].locked = value;
38686         if(!suppressEvent){
38687             this.fireEvent("columnlockchange", this, colIndex, value);
38688         }
38689     },
38690
38691     getTotalLockedWidth : function(){
38692         var totalWidth = 0;
38693         for(var i = 0; i < this.config.length; i++){
38694             if(this.isLocked(i) && !this.isHidden(i)){
38695                 this.totalWidth += this.getColumnWidth(i);
38696             }
38697         }
38698         return totalWidth;
38699     },
38700
38701     getLockedCount : function(){
38702         for(var i = 0, len = this.config.length; i < len; i++){
38703             if(!this.isLocked(i)){
38704                 return i;
38705             }
38706         }
38707     },
38708
38709     /**
38710      * Returns the number of columns.
38711      * @return {Number}
38712      */
38713     getColumnCount : function(visibleOnly){
38714         if(visibleOnly === true){
38715             var c = 0;
38716             for(var i = 0, len = this.config.length; i < len; i++){
38717                 if(!this.isHidden(i)){
38718                     c++;
38719                 }
38720             }
38721             return c;
38722         }
38723         return this.config.length;
38724     },
38725
38726     /**
38727      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38728      * @param {Function} fn
38729      * @param {Object} scope (optional)
38730      * @return {Array} result
38731      */
38732     getColumnsBy : function(fn, scope){
38733         var r = [];
38734         for(var i = 0, len = this.config.length; i < len; i++){
38735             var c = this.config[i];
38736             if(fn.call(scope||this, c, i) === true){
38737                 r[r.length] = c;
38738             }
38739         }
38740         return r;
38741     },
38742
38743     /**
38744      * Returns true if the specified column is sortable.
38745      * @param {Number} col The column index
38746      * @return {Boolean}
38747      */
38748     isSortable : function(col){
38749         if(typeof this.config[col].sortable == "undefined"){
38750             return this.defaultSortable;
38751         }
38752         return this.config[col].sortable;
38753     },
38754
38755     /**
38756      * Returns the rendering (formatting) function defined for the column.
38757      * @param {Number} col The column index.
38758      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38759      */
38760     getRenderer : function(col){
38761         if(!this.config[col].renderer){
38762             return Roo.grid.ColumnModel.defaultRenderer;
38763         }
38764         return this.config[col].renderer;
38765     },
38766
38767     /**
38768      * Sets the rendering (formatting) function for a column.
38769      * @param {Number} col The column index
38770      * @param {Function} fn The function to use to process the cell's raw data
38771      * to return HTML markup for the grid view. The render function is called with
38772      * the following parameters:<ul>
38773      * <li>Data value.</li>
38774      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38775      * <li>css A CSS style string to apply to the table cell.</li>
38776      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38777      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38778      * <li>Row index</li>
38779      * <li>Column index</li>
38780      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38781      */
38782     setRenderer : function(col, fn){
38783         this.config[col].renderer = fn;
38784     },
38785
38786     /**
38787      * Returns the width for the specified column.
38788      * @param {Number} col The column index
38789      * @return {Number}
38790      */
38791     getColumnWidth : function(col){
38792         return this.config[col].width * 1 || this.defaultWidth;
38793     },
38794
38795     /**
38796      * Sets the width for a column.
38797      * @param {Number} col The column index
38798      * @param {Number} width The new width
38799      */
38800     setColumnWidth : function(col, width, suppressEvent){
38801         this.config[col].width = width;
38802         this.totalWidth = null;
38803         if(!suppressEvent){
38804              this.fireEvent("widthchange", this, col, width);
38805         }
38806     },
38807
38808     /**
38809      * Returns the total width of all columns.
38810      * @param {Boolean} includeHidden True to include hidden column widths
38811      * @return {Number}
38812      */
38813     getTotalWidth : function(includeHidden){
38814         if(!this.totalWidth){
38815             this.totalWidth = 0;
38816             for(var i = 0, len = this.config.length; i < len; i++){
38817                 if(includeHidden || !this.isHidden(i)){
38818                     this.totalWidth += this.getColumnWidth(i);
38819                 }
38820             }
38821         }
38822         return this.totalWidth;
38823     },
38824
38825     /**
38826      * Returns the header for the specified column.
38827      * @param {Number} col The column index
38828      * @return {String}
38829      */
38830     getColumnHeader : function(col){
38831         return this.config[col].header;
38832     },
38833
38834     /**
38835      * Sets the header for a column.
38836      * @param {Number} col The column index
38837      * @param {String} header The new header
38838      */
38839     setColumnHeader : function(col, header){
38840         this.config[col].header = header;
38841         this.fireEvent("headerchange", this, col, header);
38842     },
38843
38844     /**
38845      * Returns the tooltip for the specified column.
38846      * @param {Number} col The column index
38847      * @return {String}
38848      */
38849     getColumnTooltip : function(col){
38850             return this.config[col].tooltip;
38851     },
38852     /**
38853      * Sets the tooltip for a column.
38854      * @param {Number} col The column index
38855      * @param {String} tooltip The new tooltip
38856      */
38857     setColumnTooltip : function(col, tooltip){
38858             this.config[col].tooltip = tooltip;
38859     },
38860
38861     /**
38862      * Returns the dataIndex for the specified column.
38863      * @param {Number} col The column index
38864      * @return {Number}
38865      */
38866     getDataIndex : function(col){
38867         return this.config[col].dataIndex;
38868     },
38869
38870     /**
38871      * Sets the dataIndex for a column.
38872      * @param {Number} col The column index
38873      * @param {Number} dataIndex The new dataIndex
38874      */
38875     setDataIndex : function(col, dataIndex){
38876         this.config[col].dataIndex = dataIndex;
38877     },
38878
38879     
38880     
38881     /**
38882      * Returns true if the cell is editable.
38883      * @param {Number} colIndex The column index
38884      * @param {Number} rowIndex The row index
38885      * @return {Boolean}
38886      */
38887     isCellEditable : function(colIndex, rowIndex){
38888         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38889     },
38890
38891     /**
38892      * Returns the editor defined for the cell/column.
38893      * return false or null to disable editing.
38894      * @param {Number} colIndex The column index
38895      * @param {Number} rowIndex The row index
38896      * @return {Object}
38897      */
38898     getCellEditor : function(colIndex, rowIndex){
38899         return this.config[colIndex].editor;
38900     },
38901
38902     /**
38903      * Sets if a column is editable.
38904      * @param {Number} col The column index
38905      * @param {Boolean} editable True if the column is editable
38906      */
38907     setEditable : function(col, editable){
38908         this.config[col].editable = editable;
38909     },
38910
38911
38912     /**
38913      * Returns true if the column is hidden.
38914      * @param {Number} colIndex The column index
38915      * @return {Boolean}
38916      */
38917     isHidden : function(colIndex){
38918         return this.config[colIndex].hidden;
38919     },
38920
38921
38922     /**
38923      * Returns true if the column width cannot be changed
38924      */
38925     isFixed : function(colIndex){
38926         return this.config[colIndex].fixed;
38927     },
38928
38929     /**
38930      * Returns true if the column can be resized
38931      * @return {Boolean}
38932      */
38933     isResizable : function(colIndex){
38934         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38935     },
38936     /**
38937      * Sets if a column is hidden.
38938      * @param {Number} colIndex The column index
38939      * @param {Boolean} hidden True if the column is hidden
38940      */
38941     setHidden : function(colIndex, hidden){
38942         this.config[colIndex].hidden = hidden;
38943         this.totalWidth = null;
38944         this.fireEvent("hiddenchange", this, colIndex, hidden);
38945     },
38946
38947     /**
38948      * Sets the editor for a column.
38949      * @param {Number} col The column index
38950      * @param {Object} editor The editor object
38951      */
38952     setEditor : function(col, editor){
38953         this.config[col].editor = editor;
38954     }
38955 });
38956
38957 Roo.grid.ColumnModel.defaultRenderer = function(value){
38958         if(typeof value == "string" && value.length < 1){
38959             return "&#160;";
38960         }
38961         return value;
38962 };
38963
38964 // Alias for backwards compatibility
38965 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38966 /*
38967  * Based on:
38968  * Ext JS Library 1.1.1
38969  * Copyright(c) 2006-2007, Ext JS, LLC.
38970  *
38971  * Originally Released Under LGPL - original licence link has changed is not relivant.
38972  *
38973  * Fork - LGPL
38974  * <script type="text/javascript">
38975  */
38976
38977 /**
38978  * @class Roo.grid.AbstractSelectionModel
38979  * @extends Roo.util.Observable
38980  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38981  * implemented by descendant classes.  This class should not be directly instantiated.
38982  * @constructor
38983  */
38984 Roo.grid.AbstractSelectionModel = function(){
38985     this.locked = false;
38986     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38987 };
38988
38989 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38990     /** @ignore Called by the grid automatically. Do not call directly. */
38991     init : function(grid){
38992         this.grid = grid;
38993         this.initEvents();
38994     },
38995
38996     /**
38997      * Locks the selections.
38998      */
38999     lock : function(){
39000         this.locked = true;
39001     },
39002
39003     /**
39004      * Unlocks the selections.
39005      */
39006     unlock : function(){
39007         this.locked = false;
39008     },
39009
39010     /**
39011      * Returns true if the selections are locked.
39012      * @return {Boolean}
39013      */
39014     isLocked : function(){
39015         return this.locked;
39016     }
39017 });/*
39018  * Based on:
39019  * Ext JS Library 1.1.1
39020  * Copyright(c) 2006-2007, Ext JS, LLC.
39021  *
39022  * Originally Released Under LGPL - original licence link has changed is not relivant.
39023  *
39024  * Fork - LGPL
39025  * <script type="text/javascript">
39026  */
39027 /**
39028  * @extends Roo.grid.AbstractSelectionModel
39029  * @class Roo.grid.RowSelectionModel
39030  * The default SelectionModel used by {@link Roo.grid.Grid}.
39031  * It supports multiple selections and keyboard selection/navigation. 
39032  * @constructor
39033  * @param {Object} config
39034  */
39035 Roo.grid.RowSelectionModel = function(config){
39036     Roo.apply(this, config);
39037     this.selections = new Roo.util.MixedCollection(false, function(o){
39038         return o.id;
39039     });
39040
39041     this.last = false;
39042     this.lastActive = false;
39043
39044     this.addEvents({
39045         /**
39046              * @event selectionchange
39047              * Fires when the selection changes
39048              * @param {SelectionModel} this
39049              */
39050             "selectionchange" : true,
39051         /**
39052              * @event afterselectionchange
39053              * Fires after the selection changes (eg. by key press or clicking)
39054              * @param {SelectionModel} this
39055              */
39056             "afterselectionchange" : true,
39057         /**
39058              * @event beforerowselect
39059              * Fires when a row is selected being selected, return false to cancel.
39060              * @param {SelectionModel} this
39061              * @param {Number} rowIndex The selected index
39062              * @param {Boolean} keepExisting False if other selections will be cleared
39063              */
39064             "beforerowselect" : true,
39065         /**
39066              * @event rowselect
39067              * Fires when a row is selected.
39068              * @param {SelectionModel} this
39069              * @param {Number} rowIndex The selected index
39070              * @param {Roo.data.Record} r The record
39071              */
39072             "rowselect" : true,
39073         /**
39074              * @event rowdeselect
39075              * Fires when a row is deselected.
39076              * @param {SelectionModel} this
39077              * @param {Number} rowIndex The selected index
39078              */
39079         "rowdeselect" : true
39080     });
39081     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39082     this.locked = false;
39083 };
39084
39085 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39086     /**
39087      * @cfg {Boolean} singleSelect
39088      * True to allow selection of only one row at a time (defaults to false)
39089      */
39090     singleSelect : false,
39091
39092     // private
39093     initEvents : function(){
39094
39095         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39096             this.grid.on("mousedown", this.handleMouseDown, this);
39097         }else{ // allow click to work like normal
39098             this.grid.on("rowclick", this.handleDragableRowClick, this);
39099         }
39100
39101         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39102             "up" : function(e){
39103                 if(!e.shiftKey){
39104                     this.selectPrevious(e.shiftKey);
39105                 }else if(this.last !== false && this.lastActive !== false){
39106                     var last = this.last;
39107                     this.selectRange(this.last,  this.lastActive-1);
39108                     this.grid.getView().focusRow(this.lastActive);
39109                     if(last !== false){
39110                         this.last = last;
39111                     }
39112                 }else{
39113                     this.selectFirstRow();
39114                 }
39115                 this.fireEvent("afterselectionchange", this);
39116             },
39117             "down" : function(e){
39118                 if(!e.shiftKey){
39119                     this.selectNext(e.shiftKey);
39120                 }else if(this.last !== false && this.lastActive !== false){
39121                     var last = this.last;
39122                     this.selectRange(this.last,  this.lastActive+1);
39123                     this.grid.getView().focusRow(this.lastActive);
39124                     if(last !== false){
39125                         this.last = last;
39126                     }
39127                 }else{
39128                     this.selectFirstRow();
39129                 }
39130                 this.fireEvent("afterselectionchange", this);
39131             },
39132             scope: this
39133         });
39134
39135         var view = this.grid.view;
39136         view.on("refresh", this.onRefresh, this);
39137         view.on("rowupdated", this.onRowUpdated, this);
39138         view.on("rowremoved", this.onRemove, this);
39139     },
39140
39141     // private
39142     onRefresh : function(){
39143         var ds = this.grid.dataSource, i, v = this.grid.view;
39144         var s = this.selections;
39145         s.each(function(r){
39146             if((i = ds.indexOfId(r.id)) != -1){
39147                 v.onRowSelect(i);
39148             }else{
39149                 s.remove(r);
39150             }
39151         });
39152     },
39153
39154     // private
39155     onRemove : function(v, index, r){
39156         this.selections.remove(r);
39157     },
39158
39159     // private
39160     onRowUpdated : function(v, index, r){
39161         if(this.isSelected(r)){
39162             v.onRowSelect(index);
39163         }
39164     },
39165
39166     /**
39167      * Select records.
39168      * @param {Array} records The records to select
39169      * @param {Boolean} keepExisting (optional) True to keep existing selections
39170      */
39171     selectRecords : function(records, keepExisting){
39172         if(!keepExisting){
39173             this.clearSelections();
39174         }
39175         var ds = this.grid.dataSource;
39176         for(var i = 0, len = records.length; i < len; i++){
39177             this.selectRow(ds.indexOf(records[i]), true);
39178         }
39179     },
39180
39181     /**
39182      * Gets the number of selected rows.
39183      * @return {Number}
39184      */
39185     getCount : function(){
39186         return this.selections.length;
39187     },
39188
39189     /**
39190      * Selects the first row in the grid.
39191      */
39192     selectFirstRow : function(){
39193         this.selectRow(0);
39194     },
39195
39196     /**
39197      * Select the last row.
39198      * @param {Boolean} keepExisting (optional) True to keep existing selections
39199      */
39200     selectLastRow : function(keepExisting){
39201         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39202     },
39203
39204     /**
39205      * Selects the row immediately following the last selected row.
39206      * @param {Boolean} keepExisting (optional) True to keep existing selections
39207      */
39208     selectNext : function(keepExisting){
39209         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39210             this.selectRow(this.last+1, keepExisting);
39211             this.grid.getView().focusRow(this.last);
39212         }
39213     },
39214
39215     /**
39216      * Selects the row that precedes the last selected row.
39217      * @param {Boolean} keepExisting (optional) True to keep existing selections
39218      */
39219     selectPrevious : function(keepExisting){
39220         if(this.last){
39221             this.selectRow(this.last-1, keepExisting);
39222             this.grid.getView().focusRow(this.last);
39223         }
39224     },
39225
39226     /**
39227      * Returns the selected records
39228      * @return {Array} Array of selected records
39229      */
39230     getSelections : function(){
39231         return [].concat(this.selections.items);
39232     },
39233
39234     /**
39235      * Returns the first selected record.
39236      * @return {Record}
39237      */
39238     getSelected : function(){
39239         return this.selections.itemAt(0);
39240     },
39241
39242
39243     /**
39244      * Clears all selections.
39245      */
39246     clearSelections : function(fast){
39247         if(this.locked) return;
39248         if(fast !== true){
39249             var ds = this.grid.dataSource;
39250             var s = this.selections;
39251             s.each(function(r){
39252                 this.deselectRow(ds.indexOfId(r.id));
39253             }, this);
39254             s.clear();
39255         }else{
39256             this.selections.clear();
39257         }
39258         this.last = false;
39259     },
39260
39261
39262     /**
39263      * Selects all rows.
39264      */
39265     selectAll : function(){
39266         if(this.locked) return;
39267         this.selections.clear();
39268         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39269             this.selectRow(i, true);
39270         }
39271     },
39272
39273     /**
39274      * Returns True if there is a selection.
39275      * @return {Boolean}
39276      */
39277     hasSelection : function(){
39278         return this.selections.length > 0;
39279     },
39280
39281     /**
39282      * Returns True if the specified row is selected.
39283      * @param {Number/Record} record The record or index of the record to check
39284      * @return {Boolean}
39285      */
39286     isSelected : function(index){
39287         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39288         return (r && this.selections.key(r.id) ? true : false);
39289     },
39290
39291     /**
39292      * Returns True if the specified record id is selected.
39293      * @param {String} id The id of record to check
39294      * @return {Boolean}
39295      */
39296     isIdSelected : function(id){
39297         return (this.selections.key(id) ? true : false);
39298     },
39299
39300     // private
39301     handleMouseDown : function(e, t){
39302         var view = this.grid.getView(), rowIndex;
39303         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39304             return;
39305         };
39306         if(e.shiftKey && this.last !== false){
39307             var last = this.last;
39308             this.selectRange(last, rowIndex, e.ctrlKey);
39309             this.last = last; // reset the last
39310             view.focusRow(rowIndex);
39311         }else{
39312             var isSelected = this.isSelected(rowIndex);
39313             if(e.button !== 0 && isSelected){
39314                 view.focusRow(rowIndex);
39315             }else if(e.ctrlKey && isSelected){
39316                 this.deselectRow(rowIndex);
39317             }else if(!isSelected){
39318                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39319                 view.focusRow(rowIndex);
39320             }
39321         }
39322         this.fireEvent("afterselectionchange", this);
39323     },
39324     // private
39325     handleDragableRowClick :  function(grid, rowIndex, e) 
39326     {
39327         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39328             this.selectRow(rowIndex, false);
39329             grid.view.focusRow(rowIndex);
39330              this.fireEvent("afterselectionchange", this);
39331         }
39332     },
39333     
39334     /**
39335      * Selects multiple rows.
39336      * @param {Array} rows Array of the indexes of the row to select
39337      * @param {Boolean} keepExisting (optional) True to keep existing selections
39338      */
39339     selectRows : function(rows, keepExisting){
39340         if(!keepExisting){
39341             this.clearSelections();
39342         }
39343         for(var i = 0, len = rows.length; i < len; i++){
39344             this.selectRow(rows[i], true);
39345         }
39346     },
39347
39348     /**
39349      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39350      * @param {Number} startRow The index of the first row in the range
39351      * @param {Number} endRow The index of the last row in the range
39352      * @param {Boolean} keepExisting (optional) True to retain existing selections
39353      */
39354     selectRange : function(startRow, endRow, keepExisting){
39355         if(this.locked) return;
39356         if(!keepExisting){
39357             this.clearSelections();
39358         }
39359         if(startRow <= endRow){
39360             for(var i = startRow; i <= endRow; i++){
39361                 this.selectRow(i, true);
39362             }
39363         }else{
39364             for(var i = startRow; i >= endRow; i--){
39365                 this.selectRow(i, true);
39366             }
39367         }
39368     },
39369
39370     /**
39371      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39372      * @param {Number} startRow The index of the first row in the range
39373      * @param {Number} endRow The index of the last row in the range
39374      */
39375     deselectRange : function(startRow, endRow, preventViewNotify){
39376         if(this.locked) return;
39377         for(var i = startRow; i <= endRow; i++){
39378             this.deselectRow(i, preventViewNotify);
39379         }
39380     },
39381
39382     /**
39383      * Selects a row.
39384      * @param {Number} row The index of the row to select
39385      * @param {Boolean} keepExisting (optional) True to keep existing selections
39386      */
39387     selectRow : function(index, keepExisting, preventViewNotify){
39388         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39389         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39390             if(!keepExisting || this.singleSelect){
39391                 this.clearSelections();
39392             }
39393             var r = this.grid.dataSource.getAt(index);
39394             this.selections.add(r);
39395             this.last = this.lastActive = index;
39396             if(!preventViewNotify){
39397                 this.grid.getView().onRowSelect(index);
39398             }
39399             this.fireEvent("rowselect", this, index, r);
39400             this.fireEvent("selectionchange", this);
39401         }
39402     },
39403
39404     /**
39405      * Deselects a row.
39406      * @param {Number} row The index of the row to deselect
39407      */
39408     deselectRow : function(index, preventViewNotify){
39409         if(this.locked) return;
39410         if(this.last == index){
39411             this.last = false;
39412         }
39413         if(this.lastActive == index){
39414             this.lastActive = false;
39415         }
39416         var r = this.grid.dataSource.getAt(index);
39417         this.selections.remove(r);
39418         if(!preventViewNotify){
39419             this.grid.getView().onRowDeselect(index);
39420         }
39421         this.fireEvent("rowdeselect", this, index);
39422         this.fireEvent("selectionchange", this);
39423     },
39424
39425     // private
39426     restoreLast : function(){
39427         if(this._last){
39428             this.last = this._last;
39429         }
39430     },
39431
39432     // private
39433     acceptsNav : function(row, col, cm){
39434         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39435     },
39436
39437     // private
39438     onEditorKey : function(field, e){
39439         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39440         if(k == e.TAB){
39441             e.stopEvent();
39442             ed.completeEdit();
39443             if(e.shiftKey){
39444                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39445             }else{
39446                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39447             }
39448         }else if(k == e.ENTER && !e.ctrlKey){
39449             e.stopEvent();
39450             ed.completeEdit();
39451             if(e.shiftKey){
39452                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39453             }else{
39454                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39455             }
39456         }else if(k == e.ESC){
39457             ed.cancelEdit();
39458         }
39459         if(newCell){
39460             g.startEditing(newCell[0], newCell[1]);
39461         }
39462     }
39463 });/*
39464  * Based on:
39465  * Ext JS Library 1.1.1
39466  * Copyright(c) 2006-2007, Ext JS, LLC.
39467  *
39468  * Originally Released Under LGPL - original licence link has changed is not relivant.
39469  *
39470  * Fork - LGPL
39471  * <script type="text/javascript">
39472  */
39473 /**
39474  * @class Roo.grid.CellSelectionModel
39475  * @extends Roo.grid.AbstractSelectionModel
39476  * This class provides the basic implementation for cell selection in a grid.
39477  * @constructor
39478  * @param {Object} config The object containing the configuration of this model.
39479  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39480  */
39481 Roo.grid.CellSelectionModel = function(config){
39482     Roo.apply(this, config);
39483
39484     this.selection = null;
39485
39486     this.addEvents({
39487         /**
39488              * @event beforerowselect
39489              * Fires before a cell is selected.
39490              * @param {SelectionModel} this
39491              * @param {Number} rowIndex The selected row index
39492              * @param {Number} colIndex The selected cell index
39493              */
39494             "beforecellselect" : true,
39495         /**
39496              * @event cellselect
39497              * Fires when a cell is selected.
39498              * @param {SelectionModel} this
39499              * @param {Number} rowIndex The selected row index
39500              * @param {Number} colIndex The selected cell index
39501              */
39502             "cellselect" : true,
39503         /**
39504              * @event selectionchange
39505              * Fires when the active selection changes.
39506              * @param {SelectionModel} this
39507              * @param {Object} selection null for no selection or an object (o) with two properties
39508                 <ul>
39509                 <li>o.record: the record object for the row the selection is in</li>
39510                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39511                 </ul>
39512              */
39513             "selectionchange" : true,
39514         /**
39515              * @event tabend
39516              * Fires when the tab (or enter) was pressed on the last editable cell
39517              * You can use this to trigger add new row.
39518              * @param {SelectionModel} this
39519              */
39520             "tabend" : true,
39521          /**
39522              * @event beforeeditnext
39523              * Fires before the next editable sell is made active
39524              * You can use this to skip to another cell or fire the tabend
39525              *    if you set cell to false
39526              * @param {Object} eventdata object : { cell : [ row, col ] } 
39527              */
39528             "beforeeditnext" : true
39529     });
39530     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39531 };
39532
39533 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39534     
39535     enter_is_tab: false,
39536
39537     /** @ignore */
39538     initEvents : function(){
39539         this.grid.on("mousedown", this.handleMouseDown, this);
39540         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39541         var view = this.grid.view;
39542         view.on("refresh", this.onViewChange, this);
39543         view.on("rowupdated", this.onRowUpdated, this);
39544         view.on("beforerowremoved", this.clearSelections, this);
39545         view.on("beforerowsinserted", this.clearSelections, this);
39546         if(this.grid.isEditor){
39547             this.grid.on("beforeedit", this.beforeEdit,  this);
39548         }
39549     },
39550
39551         //private
39552     beforeEdit : function(e){
39553         this.select(e.row, e.column, false, true, e.record);
39554     },
39555
39556         //private
39557     onRowUpdated : function(v, index, r){
39558         if(this.selection && this.selection.record == r){
39559             v.onCellSelect(index, this.selection.cell[1]);
39560         }
39561     },
39562
39563         //private
39564     onViewChange : function(){
39565         this.clearSelections(true);
39566     },
39567
39568         /**
39569          * Returns the currently selected cell,.
39570          * @return {Array} The selected cell (row, column) or null if none selected.
39571          */
39572     getSelectedCell : function(){
39573         return this.selection ? this.selection.cell : null;
39574     },
39575
39576     /**
39577      * Clears all selections.
39578      * @param {Boolean} true to prevent the gridview from being notified about the change.
39579      */
39580     clearSelections : function(preventNotify){
39581         var s = this.selection;
39582         if(s){
39583             if(preventNotify !== true){
39584                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39585             }
39586             this.selection = null;
39587             this.fireEvent("selectionchange", this, null);
39588         }
39589     },
39590
39591     /**
39592      * Returns true if there is a selection.
39593      * @return {Boolean}
39594      */
39595     hasSelection : function(){
39596         return this.selection ? true : false;
39597     },
39598
39599     /** @ignore */
39600     handleMouseDown : function(e, t){
39601         var v = this.grid.getView();
39602         if(this.isLocked()){
39603             return;
39604         };
39605         var row = v.findRowIndex(t);
39606         var cell = v.findCellIndex(t);
39607         if(row !== false && cell !== false){
39608             this.select(row, cell);
39609         }
39610     },
39611
39612     /**
39613      * Selects a cell.
39614      * @param {Number} rowIndex
39615      * @param {Number} collIndex
39616      */
39617     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39618         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39619             this.clearSelections();
39620             r = r || this.grid.dataSource.getAt(rowIndex);
39621             this.selection = {
39622                 record : r,
39623                 cell : [rowIndex, colIndex]
39624             };
39625             if(!preventViewNotify){
39626                 var v = this.grid.getView();
39627                 v.onCellSelect(rowIndex, colIndex);
39628                 if(preventFocus !== true){
39629                     v.focusCell(rowIndex, colIndex);
39630                 }
39631             }
39632             this.fireEvent("cellselect", this, rowIndex, colIndex);
39633             this.fireEvent("selectionchange", this, this.selection);
39634         }
39635     },
39636
39637         //private
39638     isSelectable : function(rowIndex, colIndex, cm){
39639         return !cm.isHidden(colIndex);
39640     },
39641
39642     /** @ignore */
39643     handleKeyDown : function(e){
39644         //Roo.log('Cell Sel Model handleKeyDown');
39645         if(!e.isNavKeyPress()){
39646             return;
39647         }
39648         var g = this.grid, s = this.selection;
39649         if(!s){
39650             e.stopEvent();
39651             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39652             if(cell){
39653                 this.select(cell[0], cell[1]);
39654             }
39655             return;
39656         }
39657         var sm = this;
39658         var walk = function(row, col, step){
39659             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39660         };
39661         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39662         var newCell;
39663
39664       
39665
39666         switch(k){
39667             case e.TAB:
39668                 // handled by onEditorKey
39669                 if (g.isEditor && g.editing) {
39670                     return;
39671                 }
39672                 if(e.shiftKey) {
39673                     newCell = walk(r, c-1, -1);
39674                 } else {
39675                     newCell = walk(r, c+1, 1);
39676                 }
39677                 break;
39678             
39679             case e.DOWN:
39680                newCell = walk(r+1, c, 1);
39681                 break;
39682             
39683             case e.UP:
39684                 newCell = walk(r-1, c, -1);
39685                 break;
39686             
39687             case e.RIGHT:
39688                 newCell = walk(r, c+1, 1);
39689                 break;
39690             
39691             case e.LEFT:
39692                 newCell = walk(r, c-1, -1);
39693                 break;
39694             
39695             case e.ENTER:
39696                 
39697                 if(g.isEditor && !g.editing){
39698                    g.startEditing(r, c);
39699                    e.stopEvent();
39700                    return;
39701                 }
39702                 
39703                 
39704              break;
39705         };
39706         if(newCell){
39707             this.select(newCell[0], newCell[1]);
39708             e.stopEvent();
39709             
39710         }
39711     },
39712
39713     acceptsNav : function(row, col, cm){
39714         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39715     },
39716     /**
39717      * Selects a cell.
39718      * @param {Number} field (not used) - as it's normally used as a listener
39719      * @param {Number} e - event - fake it by using
39720      *
39721      * var e = Roo.EventObjectImpl.prototype;
39722      * e.keyCode = e.TAB
39723      *
39724      * 
39725      */
39726     onEditorKey : function(field, e){
39727         
39728         var k = e.getKey(),
39729             newCell,
39730             g = this.grid,
39731             ed = g.activeEditor,
39732             forward = false;
39733         ///Roo.log('onEditorKey' + k);
39734         
39735         
39736         if (this.enter_is_tab && k == e.ENTER) {
39737             k = e.TAB;
39738         }
39739         
39740         if(k == e.TAB){
39741             if(e.shiftKey){
39742                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39743             }else{
39744                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39745                 forward = true;
39746             }
39747             
39748             e.stopEvent();
39749             
39750         } else if(k == e.ENTER &&  !e.ctrlKey){
39751             ed.completeEdit();
39752             e.stopEvent();
39753             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39754         
39755                 } else if(k == e.ESC){
39756             ed.cancelEdit();
39757         }
39758                 
39759         if (newCell) {
39760             var ecall = { cell : newCell, forward : forward };
39761             this.fireEvent('beforeeditnext', ecall );
39762             newCell = ecall.cell;
39763                         forward = ecall.forward;
39764         }
39765                 
39766         if(newCell){
39767             //Roo.log('next cell after edit');
39768             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39769         } else if (forward) {
39770             // tabbed past last
39771             this.fireEvent.defer(100, this, ['tabend',this]);
39772         }
39773     }
39774 });/*
39775  * Based on:
39776  * Ext JS Library 1.1.1
39777  * Copyright(c) 2006-2007, Ext JS, LLC.
39778  *
39779  * Originally Released Under LGPL - original licence link has changed is not relivant.
39780  *
39781  * Fork - LGPL
39782  * <script type="text/javascript">
39783  */
39784  
39785 /**
39786  * @class Roo.grid.EditorGrid
39787  * @extends Roo.grid.Grid
39788  * Class for creating and editable grid.
39789  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39790  * The container MUST have some type of size defined for the grid to fill. The container will be 
39791  * automatically set to position relative if it isn't already.
39792  * @param {Object} dataSource The data model to bind to
39793  * @param {Object} colModel The column model with info about this grid's columns
39794  */
39795 Roo.grid.EditorGrid = function(container, config){
39796     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39797     this.getGridEl().addClass("xedit-grid");
39798
39799     if(!this.selModel){
39800         this.selModel = new Roo.grid.CellSelectionModel();
39801     }
39802
39803     this.activeEditor = null;
39804
39805         this.addEvents({
39806             /**
39807              * @event beforeedit
39808              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39809              * <ul style="padding:5px;padding-left:16px;">
39810              * <li>grid - This grid</li>
39811              * <li>record - The record being edited</li>
39812              * <li>field - The field name being edited</li>
39813              * <li>value - The value for the field being edited.</li>
39814              * <li>row - The grid row index</li>
39815              * <li>column - The grid column index</li>
39816              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39817              * </ul>
39818              * @param {Object} e An edit event (see above for description)
39819              */
39820             "beforeedit" : true,
39821             /**
39822              * @event afteredit
39823              * Fires after a cell is edited. <br />
39824              * <ul style="padding:5px;padding-left:16px;">
39825              * <li>grid - This grid</li>
39826              * <li>record - The record being edited</li>
39827              * <li>field - The field name being edited</li>
39828              * <li>value - The value being set</li>
39829              * <li>originalValue - The original value for the field, before the edit.</li>
39830              * <li>row - The grid row index</li>
39831              * <li>column - The grid column index</li>
39832              * </ul>
39833              * @param {Object} e An edit event (see above for description)
39834              */
39835             "afteredit" : true,
39836             /**
39837              * @event validateedit
39838              * Fires after a cell is edited, but before the value is set in the record. 
39839          * You can use this to modify the value being set in the field, Return false
39840              * to cancel the change. The edit event object has the following properties <br />
39841              * <ul style="padding:5px;padding-left:16px;">
39842          * <li>editor - This editor</li>
39843              * <li>grid - This grid</li>
39844              * <li>record - The record being edited</li>
39845              * <li>field - The field name being edited</li>
39846              * <li>value - The value being set</li>
39847              * <li>originalValue - The original value for the field, before the edit.</li>
39848              * <li>row - The grid row index</li>
39849              * <li>column - The grid column index</li>
39850              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39851              * </ul>
39852              * @param {Object} e An edit event (see above for description)
39853              */
39854             "validateedit" : true
39855         });
39856     this.on("bodyscroll", this.stopEditing,  this);
39857     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39858 };
39859
39860 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39861     /**
39862      * @cfg {Number} clicksToEdit
39863      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39864      */
39865     clicksToEdit: 2,
39866
39867     // private
39868     isEditor : true,
39869     // private
39870     trackMouseOver: false, // causes very odd FF errors
39871
39872     onCellDblClick : function(g, row, col){
39873         this.startEditing(row, col);
39874     },
39875
39876     onEditComplete : function(ed, value, startValue){
39877         this.editing = false;
39878         this.activeEditor = null;
39879         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39880         var r = ed.record;
39881         var field = this.colModel.getDataIndex(ed.col);
39882         var e = {
39883             grid: this,
39884             record: r,
39885             field: field,
39886             originalValue: startValue,
39887             value: value,
39888             row: ed.row,
39889             column: ed.col,
39890             cancel:false,
39891             editor: ed
39892         };
39893         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39894         cell.show();
39895           
39896         if(String(value) !== String(startValue)){
39897             
39898             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39899                 r.set(field, e.value);
39900                 // if we are dealing with a combo box..
39901                 // then we also set the 'name' colum to be the displayField
39902                 if (ed.field.displayField && ed.field.name) {
39903                     r.set(ed.field.name, ed.field.el.dom.value);
39904                 }
39905                 
39906                 delete e.cancel; //?? why!!!
39907                 this.fireEvent("afteredit", e);
39908             }
39909         } else {
39910             this.fireEvent("afteredit", e); // always fire it!
39911         }
39912         this.view.focusCell(ed.row, ed.col);
39913     },
39914
39915     /**
39916      * Starts editing the specified for the specified row/column
39917      * @param {Number} rowIndex
39918      * @param {Number} colIndex
39919      */
39920     startEditing : function(row, col){
39921         this.stopEditing();
39922         if(this.colModel.isCellEditable(col, row)){
39923             this.view.ensureVisible(row, col, true);
39924           
39925             var r = this.dataSource.getAt(row);
39926             var field = this.colModel.getDataIndex(col);
39927             var cell = Roo.get(this.view.getCell(row,col));
39928             var e = {
39929                 grid: this,
39930                 record: r,
39931                 field: field,
39932                 value: r.data[field],
39933                 row: row,
39934                 column: col,
39935                 cancel:false 
39936             };
39937             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39938                 this.editing = true;
39939                 var ed = this.colModel.getCellEditor(col, row);
39940                 
39941                 if (!ed) {
39942                     return;
39943                 }
39944                 if(!ed.rendered){
39945                     ed.render(ed.parentEl || document.body);
39946                 }
39947                 ed.field.reset();
39948                
39949                 cell.hide();
39950                 
39951                 (function(){ // complex but required for focus issues in safari, ie and opera
39952                     ed.row = row;
39953                     ed.col = col;
39954                     ed.record = r;
39955                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39956                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39957                     this.activeEditor = ed;
39958                     var v = r.data[field];
39959                     ed.startEdit(this.view.getCell(row, col), v);
39960                     // combo's with 'displayField and name set
39961                     if (ed.field.displayField && ed.field.name) {
39962                         ed.field.el.dom.value = r.data[ed.field.name];
39963                     }
39964                     
39965                     
39966                 }).defer(50, this);
39967             }
39968         }
39969     },
39970         
39971     /**
39972      * Stops any active editing
39973      */
39974     stopEditing : function(){
39975         if(this.activeEditor){
39976             this.activeEditor.completeEdit();
39977         }
39978         this.activeEditor = null;
39979     },
39980         
39981          /**
39982      * Called to get grid's drag proxy text, by default returns this.ddText.
39983      * @return {String}
39984      */
39985     getDragDropText : function(){
39986         var count = this.selModel.getSelectedCell() ? 1 : 0;
39987         return String.format(this.ddText, count, count == 1 ? '' : 's');
39988     }
39989         
39990 });/*
39991  * Based on:
39992  * Ext JS Library 1.1.1
39993  * Copyright(c) 2006-2007, Ext JS, LLC.
39994  *
39995  * Originally Released Under LGPL - original licence link has changed is not relivant.
39996  *
39997  * Fork - LGPL
39998  * <script type="text/javascript">
39999  */
40000
40001 // private - not really -- you end up using it !
40002 // This is a support class used internally by the Grid components
40003
40004 /**
40005  * @class Roo.grid.GridEditor
40006  * @extends Roo.Editor
40007  * Class for creating and editable grid elements.
40008  * @param {Object} config any settings (must include field)
40009  */
40010 Roo.grid.GridEditor = function(field, config){
40011     if (!config && field.field) {
40012         config = field;
40013         field = Roo.factory(config.field, Roo.form);
40014     }
40015     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40016     field.monitorTab = false;
40017 };
40018
40019 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40020     
40021     /**
40022      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40023      */
40024     
40025     alignment: "tl-tl",
40026     autoSize: "width",
40027     hideEl : false,
40028     cls: "x-small-editor x-grid-editor",
40029     shim:false,
40030     shadow:"frame"
40031 });/*
40032  * Based on:
40033  * Ext JS Library 1.1.1
40034  * Copyright(c) 2006-2007, Ext JS, LLC.
40035  *
40036  * Originally Released Under LGPL - original licence link has changed is not relivant.
40037  *
40038  * Fork - LGPL
40039  * <script type="text/javascript">
40040  */
40041   
40042
40043   
40044 Roo.grid.PropertyRecord = Roo.data.Record.create([
40045     {name:'name',type:'string'},  'value'
40046 ]);
40047
40048
40049 Roo.grid.PropertyStore = function(grid, source){
40050     this.grid = grid;
40051     this.store = new Roo.data.Store({
40052         recordType : Roo.grid.PropertyRecord
40053     });
40054     this.store.on('update', this.onUpdate,  this);
40055     if(source){
40056         this.setSource(source);
40057     }
40058     Roo.grid.PropertyStore.superclass.constructor.call(this);
40059 };
40060
40061
40062
40063 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40064     setSource : function(o){
40065         this.source = o;
40066         this.store.removeAll();
40067         var data = [];
40068         for(var k in o){
40069             if(this.isEditableValue(o[k])){
40070                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40071             }
40072         }
40073         this.store.loadRecords({records: data}, {}, true);
40074     },
40075
40076     onUpdate : function(ds, record, type){
40077         if(type == Roo.data.Record.EDIT){
40078             var v = record.data['value'];
40079             var oldValue = record.modified['value'];
40080             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40081                 this.source[record.id] = v;
40082                 record.commit();
40083                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40084             }else{
40085                 record.reject();
40086             }
40087         }
40088     },
40089
40090     getProperty : function(row){
40091        return this.store.getAt(row);
40092     },
40093
40094     isEditableValue: function(val){
40095         if(val && val instanceof Date){
40096             return true;
40097         }else if(typeof val == 'object' || typeof val == 'function'){
40098             return false;
40099         }
40100         return true;
40101     },
40102
40103     setValue : function(prop, value){
40104         this.source[prop] = value;
40105         this.store.getById(prop).set('value', value);
40106     },
40107
40108     getSource : function(){
40109         return this.source;
40110     }
40111 });
40112
40113 Roo.grid.PropertyColumnModel = function(grid, store){
40114     this.grid = grid;
40115     var g = Roo.grid;
40116     g.PropertyColumnModel.superclass.constructor.call(this, [
40117         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40118         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40119     ]);
40120     this.store = store;
40121     this.bselect = Roo.DomHelper.append(document.body, {
40122         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40123             {tag: 'option', value: 'true', html: 'true'},
40124             {tag: 'option', value: 'false', html: 'false'}
40125         ]
40126     });
40127     Roo.id(this.bselect);
40128     var f = Roo.form;
40129     this.editors = {
40130         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40131         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40132         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40133         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40134         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40135     };
40136     this.renderCellDelegate = this.renderCell.createDelegate(this);
40137     this.renderPropDelegate = this.renderProp.createDelegate(this);
40138 };
40139
40140 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40141     
40142     
40143     nameText : 'Name',
40144     valueText : 'Value',
40145     
40146     dateFormat : 'm/j/Y',
40147     
40148     
40149     renderDate : function(dateVal){
40150         return dateVal.dateFormat(this.dateFormat);
40151     },
40152
40153     renderBool : function(bVal){
40154         return bVal ? 'true' : 'false';
40155     },
40156
40157     isCellEditable : function(colIndex, rowIndex){
40158         return colIndex == 1;
40159     },
40160
40161     getRenderer : function(col){
40162         return col == 1 ?
40163             this.renderCellDelegate : this.renderPropDelegate;
40164     },
40165
40166     renderProp : function(v){
40167         return this.getPropertyName(v);
40168     },
40169
40170     renderCell : function(val){
40171         var rv = val;
40172         if(val instanceof Date){
40173             rv = this.renderDate(val);
40174         }else if(typeof val == 'boolean'){
40175             rv = this.renderBool(val);
40176         }
40177         return Roo.util.Format.htmlEncode(rv);
40178     },
40179
40180     getPropertyName : function(name){
40181         var pn = this.grid.propertyNames;
40182         return pn && pn[name] ? pn[name] : name;
40183     },
40184
40185     getCellEditor : function(colIndex, rowIndex){
40186         var p = this.store.getProperty(rowIndex);
40187         var n = p.data['name'], val = p.data['value'];
40188         
40189         if(typeof(this.grid.customEditors[n]) == 'string'){
40190             return this.editors[this.grid.customEditors[n]];
40191         }
40192         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40193             return this.grid.customEditors[n];
40194         }
40195         if(val instanceof Date){
40196             return this.editors['date'];
40197         }else if(typeof val == 'number'){
40198             return this.editors['number'];
40199         }else if(typeof val == 'boolean'){
40200             return this.editors['boolean'];
40201         }else{
40202             return this.editors['string'];
40203         }
40204     }
40205 });
40206
40207 /**
40208  * @class Roo.grid.PropertyGrid
40209  * @extends Roo.grid.EditorGrid
40210  * This class represents the  interface of a component based property grid control.
40211  * <br><br>Usage:<pre><code>
40212  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40213       
40214  });
40215  // set any options
40216  grid.render();
40217  * </code></pre>
40218   
40219  * @constructor
40220  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40221  * The container MUST have some type of size defined for the grid to fill. The container will be
40222  * automatically set to position relative if it isn't already.
40223  * @param {Object} config A config object that sets properties on this grid.
40224  */
40225 Roo.grid.PropertyGrid = function(container, config){
40226     config = config || {};
40227     var store = new Roo.grid.PropertyStore(this);
40228     this.store = store;
40229     var cm = new Roo.grid.PropertyColumnModel(this, store);
40230     store.store.sort('name', 'ASC');
40231     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40232         ds: store.store,
40233         cm: cm,
40234         enableColLock:false,
40235         enableColumnMove:false,
40236         stripeRows:false,
40237         trackMouseOver: false,
40238         clicksToEdit:1
40239     }, config));
40240     this.getGridEl().addClass('x-props-grid');
40241     this.lastEditRow = null;
40242     this.on('columnresize', this.onColumnResize, this);
40243     this.addEvents({
40244          /**
40245              * @event beforepropertychange
40246              * Fires before a property changes (return false to stop?)
40247              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40248              * @param {String} id Record Id
40249              * @param {String} newval New Value
40250          * @param {String} oldval Old Value
40251              */
40252         "beforepropertychange": true,
40253         /**
40254              * @event propertychange
40255              * Fires after a property changes
40256              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40257              * @param {String} id Record Id
40258              * @param {String} newval New Value
40259          * @param {String} oldval Old Value
40260              */
40261         "propertychange": true
40262     });
40263     this.customEditors = this.customEditors || {};
40264 };
40265 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40266     
40267      /**
40268      * @cfg {Object} customEditors map of colnames=> custom editors.
40269      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40270      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40271      * false disables editing of the field.
40272          */
40273     
40274       /**
40275      * @cfg {Object} propertyNames map of property Names to their displayed value
40276          */
40277     
40278     render : function(){
40279         Roo.grid.PropertyGrid.superclass.render.call(this);
40280         this.autoSize.defer(100, this);
40281     },
40282
40283     autoSize : function(){
40284         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40285         if(this.view){
40286             this.view.fitColumns();
40287         }
40288     },
40289
40290     onColumnResize : function(){
40291         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40292         this.autoSize();
40293     },
40294     /**
40295      * Sets the data for the Grid
40296      * accepts a Key => Value object of all the elements avaiable.
40297      * @param {Object} data  to appear in grid.
40298      */
40299     setSource : function(source){
40300         this.store.setSource(source);
40301         //this.autoSize();
40302     },
40303     /**
40304      * Gets all the data from the grid.
40305      * @return {Object} data  data stored in grid
40306      */
40307     getSource : function(){
40308         return this.store.getSource();
40309     }
40310 });/*
40311   
40312  * Licence LGPL
40313  
40314  */
40315  
40316 /**
40317  * @class Roo.grid.Calendar
40318  * @extends Roo.util.Grid
40319  * This class extends the Grid to provide a calendar widget
40320  * <br><br>Usage:<pre><code>
40321  var grid = new Roo.grid.Calendar("my-container-id", {
40322      ds: myDataStore,
40323      cm: myColModel,
40324      selModel: mySelectionModel,
40325      autoSizeColumns: true,
40326      monitorWindowResize: false,
40327      trackMouseOver: true
40328      eventstore : real data store..
40329  });
40330  // set any options
40331  grid.render();
40332   
40333   * @constructor
40334  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40335  * The container MUST have some type of size defined for the grid to fill. The container will be
40336  * automatically set to position relative if it isn't already.
40337  * @param {Object} config A config object that sets properties on this grid.
40338  */
40339 Roo.grid.Calendar = function(container, config){
40340         // initialize the container
40341         this.container = Roo.get(container);
40342         this.container.update("");
40343         this.container.setStyle("overflow", "hidden");
40344     this.container.addClass('x-grid-container');
40345
40346     this.id = this.container.id;
40347
40348     Roo.apply(this, config);
40349     // check and correct shorthanded configs
40350     
40351     var rows = [];
40352     var d =1;
40353     for (var r = 0;r < 6;r++) {
40354         
40355         rows[r]=[];
40356         for (var c =0;c < 7;c++) {
40357             rows[r][c]= '';
40358         }
40359     }
40360     if (this.eventStore) {
40361         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40362         this.eventStore.on('load',this.onLoad, this);
40363         this.eventStore.on('beforeload',this.clearEvents, this);
40364          
40365     }
40366     
40367     this.dataSource = new Roo.data.Store({
40368             proxy: new Roo.data.MemoryProxy(rows),
40369             reader: new Roo.data.ArrayReader({}, [
40370                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40371     });
40372
40373     this.dataSource.load();
40374     this.ds = this.dataSource;
40375     this.ds.xmodule = this.xmodule || false;
40376     
40377     
40378     var cellRender = function(v,x,r)
40379     {
40380         return String.format(
40381             '<div class="fc-day  fc-widget-content"><div>' +
40382                 '<div class="fc-event-container"></div>' +
40383                 '<div class="fc-day-number">{0}</div>'+
40384                 
40385                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40386             '</div></div>', v);
40387     
40388     }
40389     
40390     
40391     this.colModel = new Roo.grid.ColumnModel( [
40392         {
40393             xtype: 'ColumnModel',
40394             xns: Roo.grid,
40395             dataIndex : 'weekday0',
40396             header : 'Sunday',
40397             renderer : cellRender
40398         },
40399         {
40400             xtype: 'ColumnModel',
40401             xns: Roo.grid,
40402             dataIndex : 'weekday1',
40403             header : 'Monday',
40404             renderer : cellRender
40405         },
40406         {
40407             xtype: 'ColumnModel',
40408             xns: Roo.grid,
40409             dataIndex : 'weekday2',
40410             header : 'Tuesday',
40411             renderer : cellRender
40412         },
40413         {
40414             xtype: 'ColumnModel',
40415             xns: Roo.grid,
40416             dataIndex : 'weekday3',
40417             header : 'Wednesday',
40418             renderer : cellRender
40419         },
40420         {
40421             xtype: 'ColumnModel',
40422             xns: Roo.grid,
40423             dataIndex : 'weekday4',
40424             header : 'Thursday',
40425             renderer : cellRender
40426         },
40427         {
40428             xtype: 'ColumnModel',
40429             xns: Roo.grid,
40430             dataIndex : 'weekday5',
40431             header : 'Friday',
40432             renderer : cellRender
40433         },
40434         {
40435             xtype: 'ColumnModel',
40436             xns: Roo.grid,
40437             dataIndex : 'weekday6',
40438             header : 'Saturday',
40439             renderer : cellRender
40440         }
40441     ]);
40442     this.cm = this.colModel;
40443     this.cm.xmodule = this.xmodule || false;
40444  
40445         
40446           
40447     //this.selModel = new Roo.grid.CellSelectionModel();
40448     //this.sm = this.selModel;
40449     //this.selModel.init(this);
40450     
40451     
40452     if(this.width){
40453         this.container.setWidth(this.width);
40454     }
40455
40456     if(this.height){
40457         this.container.setHeight(this.height);
40458     }
40459     /** @private */
40460         this.addEvents({
40461         // raw events
40462         /**
40463          * @event click
40464          * The raw click event for the entire grid.
40465          * @param {Roo.EventObject} e
40466          */
40467         "click" : true,
40468         /**
40469          * @event dblclick
40470          * The raw dblclick event for the entire grid.
40471          * @param {Roo.EventObject} e
40472          */
40473         "dblclick" : true,
40474         /**
40475          * @event contextmenu
40476          * The raw contextmenu event for the entire grid.
40477          * @param {Roo.EventObject} e
40478          */
40479         "contextmenu" : true,
40480         /**
40481          * @event mousedown
40482          * The raw mousedown event for the entire grid.
40483          * @param {Roo.EventObject} e
40484          */
40485         "mousedown" : true,
40486         /**
40487          * @event mouseup
40488          * The raw mouseup event for the entire grid.
40489          * @param {Roo.EventObject} e
40490          */
40491         "mouseup" : true,
40492         /**
40493          * @event mouseover
40494          * The raw mouseover event for the entire grid.
40495          * @param {Roo.EventObject} e
40496          */
40497         "mouseover" : true,
40498         /**
40499          * @event mouseout
40500          * The raw mouseout event for the entire grid.
40501          * @param {Roo.EventObject} e
40502          */
40503         "mouseout" : true,
40504         /**
40505          * @event keypress
40506          * The raw keypress event for the entire grid.
40507          * @param {Roo.EventObject} e
40508          */
40509         "keypress" : true,
40510         /**
40511          * @event keydown
40512          * The raw keydown event for the entire grid.
40513          * @param {Roo.EventObject} e
40514          */
40515         "keydown" : true,
40516
40517         // custom events
40518
40519         /**
40520          * @event cellclick
40521          * Fires when a cell is clicked
40522          * @param {Grid} this
40523          * @param {Number} rowIndex
40524          * @param {Number} columnIndex
40525          * @param {Roo.EventObject} e
40526          */
40527         "cellclick" : true,
40528         /**
40529          * @event celldblclick
40530          * Fires when a cell is double clicked
40531          * @param {Grid} this
40532          * @param {Number} rowIndex
40533          * @param {Number} columnIndex
40534          * @param {Roo.EventObject} e
40535          */
40536         "celldblclick" : true,
40537         /**
40538          * @event rowclick
40539          * Fires when a row is clicked
40540          * @param {Grid} this
40541          * @param {Number} rowIndex
40542          * @param {Roo.EventObject} e
40543          */
40544         "rowclick" : true,
40545         /**
40546          * @event rowdblclick
40547          * Fires when a row is double clicked
40548          * @param {Grid} this
40549          * @param {Number} rowIndex
40550          * @param {Roo.EventObject} e
40551          */
40552         "rowdblclick" : true,
40553         /**
40554          * @event headerclick
40555          * Fires when a header is clicked
40556          * @param {Grid} this
40557          * @param {Number} columnIndex
40558          * @param {Roo.EventObject} e
40559          */
40560         "headerclick" : true,
40561         /**
40562          * @event headerdblclick
40563          * Fires when a header cell is double clicked
40564          * @param {Grid} this
40565          * @param {Number} columnIndex
40566          * @param {Roo.EventObject} e
40567          */
40568         "headerdblclick" : true,
40569         /**
40570          * @event rowcontextmenu
40571          * Fires when a row is right clicked
40572          * @param {Grid} this
40573          * @param {Number} rowIndex
40574          * @param {Roo.EventObject} e
40575          */
40576         "rowcontextmenu" : true,
40577         /**
40578          * @event cellcontextmenu
40579          * Fires when a cell is right clicked
40580          * @param {Grid} this
40581          * @param {Number} rowIndex
40582          * @param {Number} cellIndex
40583          * @param {Roo.EventObject} e
40584          */
40585          "cellcontextmenu" : true,
40586         /**
40587          * @event headercontextmenu
40588          * Fires when a header is right clicked
40589          * @param {Grid} this
40590          * @param {Number} columnIndex
40591          * @param {Roo.EventObject} e
40592          */
40593         "headercontextmenu" : true,
40594         /**
40595          * @event bodyscroll
40596          * Fires when the body element is scrolled
40597          * @param {Number} scrollLeft
40598          * @param {Number} scrollTop
40599          */
40600         "bodyscroll" : true,
40601         /**
40602          * @event columnresize
40603          * Fires when the user resizes a column
40604          * @param {Number} columnIndex
40605          * @param {Number} newSize
40606          */
40607         "columnresize" : true,
40608         /**
40609          * @event columnmove
40610          * Fires when the user moves a column
40611          * @param {Number} oldIndex
40612          * @param {Number} newIndex
40613          */
40614         "columnmove" : true,
40615         /**
40616          * @event startdrag
40617          * Fires when row(s) start being dragged
40618          * @param {Grid} this
40619          * @param {Roo.GridDD} dd The drag drop object
40620          * @param {event} e The raw browser event
40621          */
40622         "startdrag" : true,
40623         /**
40624          * @event enddrag
40625          * Fires when a drag operation is complete
40626          * @param {Grid} this
40627          * @param {Roo.GridDD} dd The drag drop object
40628          * @param {event} e The raw browser event
40629          */
40630         "enddrag" : true,
40631         /**
40632          * @event dragdrop
40633          * Fires when dragged row(s) are dropped on a valid DD target
40634          * @param {Grid} this
40635          * @param {Roo.GridDD} dd The drag drop object
40636          * @param {String} targetId The target drag drop object
40637          * @param {event} e The raw browser event
40638          */
40639         "dragdrop" : true,
40640         /**
40641          * @event dragover
40642          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40643          * @param {Grid} this
40644          * @param {Roo.GridDD} dd The drag drop object
40645          * @param {String} targetId The target drag drop object
40646          * @param {event} e The raw browser event
40647          */
40648         "dragover" : true,
40649         /**
40650          * @event dragenter
40651          *  Fires when the dragged row(s) first cross another DD target while being dragged
40652          * @param {Grid} this
40653          * @param {Roo.GridDD} dd The drag drop object
40654          * @param {String} targetId The target drag drop object
40655          * @param {event} e The raw browser event
40656          */
40657         "dragenter" : true,
40658         /**
40659          * @event dragout
40660          * Fires when the dragged row(s) leave another DD target while being dragged
40661          * @param {Grid} this
40662          * @param {Roo.GridDD} dd The drag drop object
40663          * @param {String} targetId The target drag drop object
40664          * @param {event} e The raw browser event
40665          */
40666         "dragout" : true,
40667         /**
40668          * @event rowclass
40669          * Fires when a row is rendered, so you can change add a style to it.
40670          * @param {GridView} gridview   The grid view
40671          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
40672          */
40673         'rowclass' : true,
40674
40675         /**
40676          * @event render
40677          * Fires when the grid is rendered
40678          * @param {Grid} grid
40679          */
40680         'render' : true,
40681             /**
40682              * @event select
40683              * Fires when a date is selected
40684              * @param {DatePicker} this
40685              * @param {Date} date The selected date
40686              */
40687         'select': true,
40688         /**
40689              * @event monthchange
40690              * Fires when the displayed month changes 
40691              * @param {DatePicker} this
40692              * @param {Date} date The selected month
40693              */
40694         'monthchange': true,
40695         /**
40696              * @event evententer
40697              * Fires when mouse over an event
40698              * @param {Calendar} this
40699              * @param {event} Event
40700              */
40701         'evententer': true,
40702         /**
40703              * @event eventleave
40704              * Fires when the mouse leaves an
40705              * @param {Calendar} this
40706              * @param {event}
40707              */
40708         'eventleave': true,
40709         /**
40710              * @event eventclick
40711              * Fires when the mouse click an
40712              * @param {Calendar} this
40713              * @param {event}
40714              */
40715         'eventclick': true,
40716         /**
40717              * @event eventrender
40718              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
40719              * @param {Calendar} this
40720              * @param {data} data to be modified
40721              */
40722         'eventrender': true
40723         
40724     });
40725
40726     Roo.grid.Grid.superclass.constructor.call(this);
40727     this.on('render', function() {
40728         this.view.el.addClass('x-grid-cal'); 
40729         
40730         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
40731
40732     },this);
40733     
40734     if (!Roo.grid.Calendar.style) {
40735         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
40736             
40737             
40738             '.x-grid-cal .x-grid-col' :  {
40739                 height: 'auto !important',
40740                 'vertical-align': 'top'
40741             },
40742             '.x-grid-cal  .fc-event-hori' : {
40743                 height: '14px'
40744             }
40745              
40746             
40747         }, Roo.id());
40748     }
40749
40750     
40751     
40752 };
40753 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
40754     /**
40755      * @cfg {Store} eventStore The store that loads events.
40756      */
40757     eventStore : 25,
40758
40759      
40760     activeDate : false,
40761     startDay : 0,
40762     autoWidth : true,
40763     monitorWindowResize : false,
40764
40765     
40766     resizeColumns : function() {
40767         var col = (this.view.el.getWidth() / 7) - 3;
40768         // loop through cols, and setWidth
40769         for(var i =0 ; i < 7 ; i++){
40770             this.cm.setColumnWidth(i, col);
40771         }
40772     },
40773      setDate :function(date) {
40774         
40775         Roo.log('setDate?');
40776         
40777         this.resizeColumns();
40778         var vd = this.activeDate;
40779         this.activeDate = date;
40780 //        if(vd && this.el){
40781 //            var t = date.getTime();
40782 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
40783 //                Roo.log('using add remove');
40784 //                
40785 //                this.fireEvent('monthchange', this, date);
40786 //                
40787 //                this.cells.removeClass("fc-state-highlight");
40788 //                this.cells.each(function(c){
40789 //                   if(c.dateValue == t){
40790 //                       c.addClass("fc-state-highlight");
40791 //                       setTimeout(function(){
40792 //                            try{c.dom.firstChild.focus();}catch(e){}
40793 //                       }, 50);
40794 //                       return false;
40795 //                   }
40796 //                   return true;
40797 //                });
40798 //                return;
40799 //            }
40800 //        }
40801         
40802         var days = date.getDaysInMonth();
40803         
40804         var firstOfMonth = date.getFirstDateOfMonth();
40805         var startingPos = firstOfMonth.getDay()-this.startDay;
40806         
40807         if(startingPos < this.startDay){
40808             startingPos += 7;
40809         }
40810         
40811         var pm = date.add(Date.MONTH, -1);
40812         var prevStart = pm.getDaysInMonth()-startingPos;
40813 //        
40814         
40815         
40816         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40817         
40818         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
40819         //this.cells.addClassOnOver('fc-state-hover');
40820         
40821         var cells = this.cells.elements;
40822         var textEls = this.textNodes;
40823         
40824         //Roo.each(cells, function(cell){
40825         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
40826         //});
40827         
40828         days += startingPos;
40829
40830         // convert everything to numbers so it's fast
40831         var day = 86400000;
40832         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
40833         //Roo.log(d);
40834         //Roo.log(pm);
40835         //Roo.log(prevStart);
40836         
40837         var today = new Date().clearTime().getTime();
40838         var sel = date.clearTime().getTime();
40839         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
40840         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
40841         var ddMatch = this.disabledDatesRE;
40842         var ddText = this.disabledDatesText;
40843         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
40844         var ddaysText = this.disabledDaysText;
40845         var format = this.format;
40846         
40847         var setCellClass = function(cal, cell){
40848             
40849             //Roo.log('set Cell Class');
40850             cell.title = "";
40851             var t = d.getTime();
40852             
40853             //Roo.log(d);
40854             
40855             
40856             cell.dateValue = t;
40857             if(t == today){
40858                 cell.className += " fc-today";
40859                 cell.className += " fc-state-highlight";
40860                 cell.title = cal.todayText;
40861             }
40862             if(t == sel){
40863                 // disable highlight in other month..
40864                 cell.className += " fc-state-highlight";
40865                 
40866             }
40867             // disabling
40868             if(t < min) {
40869                 //cell.className = " fc-state-disabled";
40870                 cell.title = cal.minText;
40871                 return;
40872             }
40873             if(t > max) {
40874                 //cell.className = " fc-state-disabled";
40875                 cell.title = cal.maxText;
40876                 return;
40877             }
40878             if(ddays){
40879                 if(ddays.indexOf(d.getDay()) != -1){
40880                     // cell.title = ddaysText;
40881                    // cell.className = " fc-state-disabled";
40882                 }
40883             }
40884             if(ddMatch && format){
40885                 var fvalue = d.dateFormat(format);
40886                 if(ddMatch.test(fvalue)){
40887                     cell.title = ddText.replace("%0", fvalue);
40888                    cell.className = " fc-state-disabled";
40889                 }
40890             }
40891             
40892             if (!cell.initialClassName) {
40893                 cell.initialClassName = cell.dom.className;
40894             }
40895             
40896             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
40897         };
40898
40899         var i = 0;
40900         
40901         for(; i < startingPos; i++) {
40902             cells[i].dayName =  (++prevStart);
40903             Roo.log(textEls[i]);
40904             d.setDate(d.getDate()+1);
40905             
40906             //cells[i].className = "fc-past fc-other-month";
40907             setCellClass(this, cells[i]);
40908         }
40909         
40910         var intDay = 0;
40911         
40912         for(; i < days; i++){
40913             intDay = i - startingPos + 1;
40914             cells[i].dayName =  (intDay);
40915             d.setDate(d.getDate()+1);
40916             
40917             cells[i].className = ''; // "x-date-active";
40918             setCellClass(this, cells[i]);
40919         }
40920         var extraDays = 0;
40921         
40922         for(; i < 42; i++) {
40923             //textEls[i].innerHTML = (++extraDays);
40924             
40925             d.setDate(d.getDate()+1);
40926             cells[i].dayName = (++extraDays);
40927             cells[i].className = "fc-future fc-other-month";
40928             setCellClass(this, cells[i]);
40929         }
40930         
40931         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
40932         
40933         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
40934         
40935         // this will cause all the cells to mis
40936         var rows= [];
40937         var i =0;
40938         for (var r = 0;r < 6;r++) {
40939             for (var c =0;c < 7;c++) {
40940                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
40941             }    
40942         }
40943         
40944         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40945         for(i=0;i<cells.length;i++) {
40946             
40947             this.cells.elements[i].dayName = cells[i].dayName ;
40948             this.cells.elements[i].className = cells[i].className;
40949             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
40950             this.cells.elements[i].title = cells[i].title ;
40951             this.cells.elements[i].dateValue = cells[i].dateValue ;
40952         }
40953         
40954         
40955         
40956         
40957         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
40958         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
40959         
40960         ////if(totalRows != 6){
40961             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
40962            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
40963        // }
40964         
40965         this.fireEvent('monthchange', this, date);
40966         
40967         
40968     },
40969  /**
40970      * Returns the grid's SelectionModel.
40971      * @return {SelectionModel}
40972      */
40973     getSelectionModel : function(){
40974         if(!this.selModel){
40975             this.selModel = new Roo.grid.CellSelectionModel();
40976         }
40977         return this.selModel;
40978     },
40979
40980     load: function() {
40981         this.eventStore.load()
40982         
40983         
40984         
40985     },
40986     
40987     findCell : function(dt) {
40988         dt = dt.clearTime().getTime();
40989         var ret = false;
40990         this.cells.each(function(c){
40991             //Roo.log("check " +c.dateValue + '?=' + dt);
40992             if(c.dateValue == dt){
40993                 ret = c;
40994                 return false;
40995             }
40996             return true;
40997         });
40998         
40999         return ret;
41000     },
41001     
41002     findCells : function(rec) {
41003         var s = rec.data.start_dt.clone().clearTime().getTime();
41004        // Roo.log(s);
41005         var e= rec.data.end_dt.clone().clearTime().getTime();
41006        // Roo.log(e);
41007         var ret = [];
41008         this.cells.each(function(c){
41009              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41010             
41011             if(c.dateValue > e){
41012                 return ;
41013             }
41014             if(c.dateValue < s){
41015                 return ;
41016             }
41017             ret.push(c);
41018         });
41019         
41020         return ret;    
41021     },
41022     
41023     findBestRow: function(cells)
41024     {
41025         var ret = 0;
41026         
41027         for (var i =0 ; i < cells.length;i++) {
41028             ret  = Math.max(cells[i].rows || 0,ret);
41029         }
41030         return ret;
41031         
41032     },
41033     
41034     
41035     addItem : function(rec)
41036     {
41037         // look for vertical location slot in
41038         var cells = this.findCells(rec);
41039         
41040         rec.row = this.findBestRow(cells);
41041         
41042         // work out the location.
41043         
41044         var crow = false;
41045         var rows = [];
41046         for(var i =0; i < cells.length; i++) {
41047             if (!crow) {
41048                 crow = {
41049                     start : cells[i],
41050                     end :  cells[i]
41051                 };
41052                 continue;
41053             }
41054             if (crow.start.getY() == cells[i].getY()) {
41055                 // on same row.
41056                 crow.end = cells[i];
41057                 continue;
41058             }
41059             // different row.
41060             rows.push(crow);
41061             crow = {
41062                 start: cells[i],
41063                 end : cells[i]
41064             };
41065             
41066         }
41067         
41068         rows.push(crow);
41069         rec.els = [];
41070         rec.rows = rows;
41071         rec.cells = cells;
41072         for (var i = 0; i < cells.length;i++) {
41073             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41074             
41075         }
41076         
41077         
41078     },
41079     
41080     clearEvents: function() {
41081         
41082         if (!this.eventStore.getCount()) {
41083             return;
41084         }
41085         // reset number of rows in cells.
41086         Roo.each(this.cells.elements, function(c){
41087             c.rows = 0;
41088         });
41089         
41090         this.eventStore.each(function(e) {
41091             this.clearEvent(e);
41092         },this);
41093         
41094     },
41095     
41096     clearEvent : function(ev)
41097     {
41098         if (ev.els) {
41099             Roo.each(ev.els, function(el) {
41100                 el.un('mouseenter' ,this.onEventEnter, this);
41101                 el.un('mouseleave' ,this.onEventLeave, this);
41102                 el.remove();
41103             },this);
41104             ev.els = [];
41105         }
41106     },
41107     
41108     
41109     renderEvent : function(ev,ctr) {
41110         if (!ctr) {
41111              ctr = this.view.el.select('.fc-event-container',true).first();
41112         }
41113         
41114          
41115         this.clearEvent(ev);
41116             //code
41117        
41118         
41119         
41120         ev.els = [];
41121         var cells = ev.cells;
41122         var rows = ev.rows;
41123         this.fireEvent('eventrender', this, ev);
41124         
41125         for(var i =0; i < rows.length; i++) {
41126             
41127             cls = '';
41128             if (i == 0) {
41129                 cls += ' fc-event-start';
41130             }
41131             if ((i+1) == rows.length) {
41132                 cls += ' fc-event-end';
41133             }
41134             
41135             //Roo.log(ev.data);
41136             // how many rows should it span..
41137             var cg = this.eventTmpl.append(ctr,Roo.apply({
41138                 fccls : cls
41139                 
41140             }, ev.data) , true);
41141             
41142             
41143             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41144             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41145             cg.on('click', this.onEventClick, this, ev);
41146             
41147             ev.els.push(cg);
41148             
41149             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41150             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41151             //Roo.log(cg);
41152              
41153             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41154             cg.setWidth(ebox.right - sbox.x -2);
41155         }
41156     },
41157     
41158     renderEvents: function()
41159     {   
41160         // first make sure there is enough space..
41161         
41162         if (!this.eventTmpl) {
41163             this.eventTmpl = new Roo.Template(
41164                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41165                     '<div class="fc-event-inner">' +
41166                         '<span class="fc-event-time">{time}</span>' +
41167                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41168                     '</div>' +
41169                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41170                 '</div>'
41171             );
41172                 
41173         }
41174                
41175         
41176         
41177         this.cells.each(function(c) {
41178             //Roo.log(c.select('.fc-day-content div',true).first());
41179             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41180         });
41181         
41182         var ctr = this.view.el.select('.fc-event-container',true).first();
41183         
41184         var cls;
41185         this.eventStore.each(function(ev){
41186             
41187             this.renderEvent(ev);
41188              
41189              
41190         }, this);
41191         this.view.layout();
41192         
41193     },
41194     
41195     onEventEnter: function (e, el,event,d) {
41196         this.fireEvent('evententer', this, el, event);
41197     },
41198     
41199     onEventLeave: function (e, el,event,d) {
41200         this.fireEvent('eventleave', this, el, event);
41201     },
41202     
41203     onEventClick: function (e, el,event,d) {
41204         this.fireEvent('eventclick', this, el, event);
41205     },
41206     
41207     onMonthChange: function () {
41208         this.store.load();
41209     },
41210     
41211     onLoad: function () {
41212         
41213         //Roo.log('calendar onload');
41214 //         
41215         if(this.eventStore.getCount() > 0){
41216             
41217            
41218             
41219             this.eventStore.each(function(d){
41220                 
41221                 
41222                 // FIXME..
41223                 var add =   d.data;
41224                 if (typeof(add.end_dt) == 'undefined')  {
41225                     Roo.log("Missing End time in calendar data: ");
41226                     Roo.log(d);
41227                     return;
41228                 }
41229                 if (typeof(add.start_dt) == 'undefined')  {
41230                     Roo.log("Missing Start time in calendar data: ");
41231                     Roo.log(d);
41232                     return;
41233                 }
41234                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41235                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41236                 add.id = add.id || d.id;
41237                 add.title = add.title || '??';
41238                 
41239                 this.addItem(d);
41240                 
41241              
41242             },this);
41243         }
41244         
41245         this.renderEvents();
41246     }
41247     
41248
41249 });
41250 /*
41251  grid : {
41252                 xtype: 'Grid',
41253                 xns: Roo.grid,
41254                 listeners : {
41255                     render : function ()
41256                     {
41257                         _this.grid = this;
41258                         
41259                         if (!this.view.el.hasClass('course-timesheet')) {
41260                             this.view.el.addClass('course-timesheet');
41261                         }
41262                         if (this.tsStyle) {
41263                             this.ds.load({});
41264                             return; 
41265                         }
41266                         Roo.log('width');
41267                         Roo.log(_this.grid.view.el.getWidth());
41268                         
41269                         
41270                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41271                             '.course-timesheet .x-grid-row' : {
41272                                 height: '80px'
41273                             },
41274                             '.x-grid-row td' : {
41275                                 'vertical-align' : 0
41276                             },
41277                             '.course-edit-link' : {
41278                                 'color' : 'blue',
41279                                 'text-overflow' : 'ellipsis',
41280                                 'overflow' : 'hidden',
41281                                 'white-space' : 'nowrap',
41282                                 'cursor' : 'pointer'
41283                             },
41284                             '.sub-link' : {
41285                                 'color' : 'green'
41286                             },
41287                             '.de-act-sup-link' : {
41288                                 'color' : 'purple',
41289                                 'text-decoration' : 'line-through'
41290                             },
41291                             '.de-act-link' : {
41292                                 'color' : 'red',
41293                                 'text-decoration' : 'line-through'
41294                             },
41295                             '.course-timesheet .course-highlight' : {
41296                                 'border-top-style': 'dashed !important',
41297                                 'border-bottom-bottom': 'dashed !important'
41298                             },
41299                             '.course-timesheet .course-item' : {
41300                                 'font-family'   : 'tahoma, arial, helvetica',
41301                                 'font-size'     : '11px',
41302                                 'overflow'      : 'hidden',
41303                                 'padding-left'  : '10px',
41304                                 'padding-right' : '10px',
41305                                 'padding-top' : '10px' 
41306                             }
41307                             
41308                         }, Roo.id());
41309                                 this.ds.load({});
41310                     }
41311                 },
41312                 autoWidth : true,
41313                 monitorWindowResize : false,
41314                 cellrenderer : function(v,x,r)
41315                 {
41316                     return v;
41317                 },
41318                 sm : {
41319                     xtype: 'CellSelectionModel',
41320                     xns: Roo.grid
41321                 },
41322                 dataSource : {
41323                     xtype: 'Store',
41324                     xns: Roo.data,
41325                     listeners : {
41326                         beforeload : function (_self, options)
41327                         {
41328                             options.params = options.params || {};
41329                             options.params._month = _this.monthField.getValue();
41330                             options.params.limit = 9999;
41331                             options.params['sort'] = 'when_dt';    
41332                             options.params['dir'] = 'ASC';    
41333                             this.proxy.loadResponse = this.loadResponse;
41334                             Roo.log("load?");
41335                             //this.addColumns();
41336                         },
41337                         load : function (_self, records, options)
41338                         {
41339                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41340                                 // if you click on the translation.. you can edit it...
41341                                 var el = Roo.get(this);
41342                                 var id = el.dom.getAttribute('data-id');
41343                                 var d = el.dom.getAttribute('data-date');
41344                                 var t = el.dom.getAttribute('data-time');
41345                                 //var id = this.child('span').dom.textContent;
41346                                 
41347                                 //Roo.log(this);
41348                                 Pman.Dialog.CourseCalendar.show({
41349                                     id : id,
41350                                     when_d : d,
41351                                     when_t : t,
41352                                     productitem_active : id ? 1 : 0
41353                                 }, function() {
41354                                     _this.grid.ds.load({});
41355                                 });
41356                            
41357                            });
41358                            
41359                            _this.panel.fireEvent('resize', [ '', '' ]);
41360                         }
41361                     },
41362                     loadResponse : function(o, success, response){
41363                             // this is overridden on before load..
41364                             
41365                             Roo.log("our code?");       
41366                             //Roo.log(success);
41367                             //Roo.log(response)
41368                             delete this.activeRequest;
41369                             if(!success){
41370                                 this.fireEvent("loadexception", this, o, response);
41371                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41372                                 return;
41373                             }
41374                             var result;
41375                             try {
41376                                 result = o.reader.read(response);
41377                             }catch(e){
41378                                 Roo.log("load exception?");
41379                                 this.fireEvent("loadexception", this, o, response, e);
41380                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41381                                 return;
41382                             }
41383                             Roo.log("ready...");        
41384                             // loop through result.records;
41385                             // and set this.tdate[date] = [] << array of records..
41386                             _this.tdata  = {};
41387                             Roo.each(result.records, function(r){
41388                                 //Roo.log(r.data);
41389                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41390                                     _this.tdata[r.data.when_dt.format('j')] = [];
41391                                 }
41392                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41393                             });
41394                             
41395                             //Roo.log(_this.tdata);
41396                             
41397                             result.records = [];
41398                             result.totalRecords = 6;
41399                     
41400                             // let's generate some duumy records for the rows.
41401                             //var st = _this.dateField.getValue();
41402                             
41403                             // work out monday..
41404                             //st = st.add(Date.DAY, -1 * st.format('w'));
41405                             
41406                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41407                             
41408                             var firstOfMonth = date.getFirstDayOfMonth();
41409                             var days = date.getDaysInMonth();
41410                             var d = 1;
41411                             var firstAdded = false;
41412                             for (var i = 0; i < result.totalRecords ; i++) {
41413                                 //var d= st.add(Date.DAY, i);
41414                                 var row = {};
41415                                 var added = 0;
41416                                 for(var w = 0 ; w < 7 ; w++){
41417                                     if(!firstAdded && firstOfMonth != w){
41418                                         continue;
41419                                     }
41420                                     if(d > days){
41421                                         continue;
41422                                     }
41423                                     firstAdded = true;
41424                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41425                                     row['weekday'+w] = String.format(
41426                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41427                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41428                                                     d,
41429                                                     date.format('Y-m-')+dd
41430                                                 );
41431                                     added++;
41432                                     if(typeof(_this.tdata[d]) != 'undefined'){
41433                                         Roo.each(_this.tdata[d], function(r){
41434                                             var is_sub = '';
41435                                             var deactive = '';
41436                                             var id = r.id;
41437                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41438                                             if(r.parent_id*1>0){
41439                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41440                                                 id = r.parent_id;
41441                                             }
41442                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41443                                                 deactive = 'de-act-link';
41444                                             }
41445                                             
41446                                             row['weekday'+w] += String.format(
41447                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41448                                                     id, //0
41449                                                     r.product_id_name, //1
41450                                                     r.when_dt.format('h:ia'), //2
41451                                                     is_sub, //3
41452                                                     deactive, //4
41453                                                     desc // 5
41454                                             );
41455                                         });
41456                                     }
41457                                     d++;
41458                                 }
41459                                 
41460                                 // only do this if something added..
41461                                 if(added > 0){ 
41462                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41463                                 }
41464                                 
41465                                 
41466                                 // push it twice. (second one with an hour..
41467                                 
41468                             }
41469                             //Roo.log(result);
41470                             this.fireEvent("load", this, o, o.request.arg);
41471                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41472                         },
41473                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41474                     proxy : {
41475                         xtype: 'HttpProxy',
41476                         xns: Roo.data,
41477                         method : 'GET',
41478                         url : baseURL + '/Roo/Shop_course.php'
41479                     },
41480                     reader : {
41481                         xtype: 'JsonReader',
41482                         xns: Roo.data,
41483                         id : 'id',
41484                         fields : [
41485                             {
41486                                 'name': 'id',
41487                                 'type': 'int'
41488                             },
41489                             {
41490                                 'name': 'when_dt',
41491                                 'type': 'string'
41492                             },
41493                             {
41494                                 'name': 'end_dt',
41495                                 'type': 'string'
41496                             },
41497                             {
41498                                 'name': 'parent_id',
41499                                 'type': 'int'
41500                             },
41501                             {
41502                                 'name': 'product_id',
41503                                 'type': 'int'
41504                             },
41505                             {
41506                                 'name': 'productitem_id',
41507                                 'type': 'int'
41508                             },
41509                             {
41510                                 'name': 'guid',
41511                                 'type': 'int'
41512                             }
41513                         ]
41514                     }
41515                 },
41516                 toolbar : {
41517                     xtype: 'Toolbar',
41518                     xns: Roo,
41519                     items : [
41520                         {
41521                             xtype: 'Button',
41522                             xns: Roo.Toolbar,
41523                             listeners : {
41524                                 click : function (_self, e)
41525                                 {
41526                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41527                                     sd.setMonth(sd.getMonth()-1);
41528                                     _this.monthField.setValue(sd.format('Y-m-d'));
41529                                     _this.grid.ds.load({});
41530                                 }
41531                             },
41532                             text : "Back"
41533                         },
41534                         {
41535                             xtype: 'Separator',
41536                             xns: Roo.Toolbar
41537                         },
41538                         {
41539                             xtype: 'MonthField',
41540                             xns: Roo.form,
41541                             listeners : {
41542                                 render : function (_self)
41543                                 {
41544                                     _this.monthField = _self;
41545                                    // _this.monthField.set  today
41546                                 },
41547                                 select : function (combo, date)
41548                                 {
41549                                     _this.grid.ds.load({});
41550                                 }
41551                             },
41552                             value : (function() { return new Date(); })()
41553                         },
41554                         {
41555                             xtype: 'Separator',
41556                             xns: Roo.Toolbar
41557                         },
41558                         {
41559                             xtype: 'TextItem',
41560                             xns: Roo.Toolbar,
41561                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41562                         },
41563                         {
41564                             xtype: 'Fill',
41565                             xns: Roo.Toolbar
41566                         },
41567                         {
41568                             xtype: 'Button',
41569                             xns: Roo.Toolbar,
41570                             listeners : {
41571                                 click : function (_self, e)
41572                                 {
41573                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41574                                     sd.setMonth(sd.getMonth()+1);
41575                                     _this.monthField.setValue(sd.format('Y-m-d'));
41576                                     _this.grid.ds.load({});
41577                                 }
41578                             },
41579                             text : "Next"
41580                         }
41581                     ]
41582                 },
41583                  
41584             }
41585         };
41586         
41587         *//*
41588  * Based on:
41589  * Ext JS Library 1.1.1
41590  * Copyright(c) 2006-2007, Ext JS, LLC.
41591  *
41592  * Originally Released Under LGPL - original licence link has changed is not relivant.
41593  *
41594  * Fork - LGPL
41595  * <script type="text/javascript">
41596  */
41597  
41598 /**
41599  * @class Roo.LoadMask
41600  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41601  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41602  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41603  * element's UpdateManager load indicator and will be destroyed after the initial load.
41604  * @constructor
41605  * Create a new LoadMask
41606  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41607  * @param {Object} config The config object
41608  */
41609 Roo.LoadMask = function(el, config){
41610     this.el = Roo.get(el);
41611     Roo.apply(this, config);
41612     if(this.store){
41613         this.store.on('beforeload', this.onBeforeLoad, this);
41614         this.store.on('load', this.onLoad, this);
41615         this.store.on('loadexception', this.onLoadException, this);
41616         this.removeMask = false;
41617     }else{
41618         var um = this.el.getUpdateManager();
41619         um.showLoadIndicator = false; // disable the default indicator
41620         um.on('beforeupdate', this.onBeforeLoad, this);
41621         um.on('update', this.onLoad, this);
41622         um.on('failure', this.onLoad, this);
41623         this.removeMask = true;
41624     }
41625 };
41626
41627 Roo.LoadMask.prototype = {
41628     /**
41629      * @cfg {Boolean} removeMask
41630      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41631      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41632      */
41633     /**
41634      * @cfg {String} msg
41635      * The text to display in a centered loading message box (defaults to 'Loading...')
41636      */
41637     msg : 'Loading...',
41638     /**
41639      * @cfg {String} msgCls
41640      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41641      */
41642     msgCls : 'x-mask-loading',
41643
41644     /**
41645      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41646      * @type Boolean
41647      */
41648     disabled: false,
41649
41650     /**
41651      * Disables the mask to prevent it from being displayed
41652      */
41653     disable : function(){
41654        this.disabled = true;
41655     },
41656
41657     /**
41658      * Enables the mask so that it can be displayed
41659      */
41660     enable : function(){
41661         this.disabled = false;
41662     },
41663     
41664     onLoadException : function()
41665     {
41666         Roo.log(arguments);
41667         
41668         if (typeof(arguments[3]) != 'undefined') {
41669             Roo.MessageBox.alert("Error loading",arguments[3]);
41670         } 
41671         /*
41672         try {
41673             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41674                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41675             }   
41676         } catch(e) {
41677             
41678         }
41679         */
41680     
41681         
41682         
41683         this.el.unmask(this.removeMask);
41684     },
41685     // private
41686     onLoad : function()
41687     {
41688         this.el.unmask(this.removeMask);
41689     },
41690
41691     // private
41692     onBeforeLoad : function(){
41693         if(!this.disabled){
41694             this.el.mask(this.msg, this.msgCls);
41695         }
41696     },
41697
41698     // private
41699     destroy : function(){
41700         if(this.store){
41701             this.store.un('beforeload', this.onBeforeLoad, this);
41702             this.store.un('load', this.onLoad, this);
41703             this.store.un('loadexception', this.onLoadException, this);
41704         }else{
41705             var um = this.el.getUpdateManager();
41706             um.un('beforeupdate', this.onBeforeLoad, this);
41707             um.un('update', this.onLoad, this);
41708             um.un('failure', this.onLoad, this);
41709         }
41710     }
41711 };/*
41712  * Based on:
41713  * Ext JS Library 1.1.1
41714  * Copyright(c) 2006-2007, Ext JS, LLC.
41715  *
41716  * Originally Released Under LGPL - original licence link has changed is not relivant.
41717  *
41718  * Fork - LGPL
41719  * <script type="text/javascript">
41720  */
41721
41722
41723 /**
41724  * @class Roo.XTemplate
41725  * @extends Roo.Template
41726  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
41727 <pre><code>
41728 var t = new Roo.XTemplate(
41729         '&lt;select name="{name}"&gt;',
41730                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
41731         '&lt;/select&gt;'
41732 );
41733  
41734 // then append, applying the master template values
41735  </code></pre>
41736  *
41737  * Supported features:
41738  *
41739  *  Tags:
41740
41741 <pre><code>
41742       {a_variable} - output encoded.
41743       {a_variable.format:("Y-m-d")} - call a method on the variable
41744       {a_variable:raw} - unencoded output
41745       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
41746       {a_variable:this.method_on_template(...)} - call a method on the template object.
41747  
41748 </code></pre>
41749  *  The tpl tag:
41750 <pre><code>
41751         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
41752         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
41753         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
41754         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
41755   
41756         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
41757         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
41758 </code></pre>
41759  *      
41760  */
41761 Roo.XTemplate = function()
41762 {
41763     Roo.XTemplate.superclass.constructor.apply(this, arguments);
41764     if (this.html) {
41765         this.compile();
41766     }
41767 };
41768
41769
41770 Roo.extend(Roo.XTemplate, Roo.Template, {
41771
41772     /**
41773      * The various sub templates
41774      */
41775     tpls : false,
41776     /**
41777      *
41778      * basic tag replacing syntax
41779      * WORD:WORD()
41780      *
41781      * // you can fake an object call by doing this
41782      *  x.t:(test,tesT) 
41783      * 
41784      */
41785     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
41786
41787     /**
41788      * compile the template
41789      *
41790      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
41791      *
41792      */
41793     compile: function()
41794     {
41795         var s = this.html;
41796      
41797         s = ['<tpl>', s, '</tpl>'].join('');
41798     
41799         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
41800             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
41801             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
41802             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
41803             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
41804             m,
41805             id     = 0,
41806             tpls   = [];
41807     
41808         while(true == !!(m = s.match(re))){
41809             var forMatch   = m[0].match(nameRe),
41810                 ifMatch   = m[0].match(ifRe),
41811                 execMatch   = m[0].match(execRe),
41812                 namedMatch   = m[0].match(namedRe),
41813                 
41814                 exp  = null, 
41815                 fn   = null,
41816                 exec = null,
41817                 name = forMatch && forMatch[1] ? forMatch[1] : '';
41818                 
41819             if (ifMatch) {
41820                 // if - puts fn into test..
41821                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
41822                 if(exp){
41823                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
41824                 }
41825             }
41826             
41827             if (execMatch) {
41828                 // exec - calls a function... returns empty if true is  returned.
41829                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
41830                 if(exp){
41831                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
41832                 }
41833             }
41834             
41835             
41836             if (name) {
41837                 // for = 
41838                 switch(name){
41839                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
41840                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
41841                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
41842                 }
41843             }
41844             var uid = namedMatch ? namedMatch[1] : id;
41845             
41846             
41847             tpls.push({
41848                 id:     namedMatch ? namedMatch[1] : id,
41849                 target: name,
41850                 exec:   exec,
41851                 test:   fn,
41852                 body:   m[1] || ''
41853             });
41854             if (namedMatch) {
41855                 s = s.replace(m[0], '');
41856             } else { 
41857                 s = s.replace(m[0], '{xtpl'+ id + '}');
41858             }
41859             ++id;
41860         }
41861         this.tpls = [];
41862         for(var i = tpls.length-1; i >= 0; --i){
41863             this.compileTpl(tpls[i]);
41864             this.tpls[tpls[i].id] = tpls[i];
41865         }
41866         this.master = tpls[tpls.length-1];
41867         return this;
41868     },
41869     /**
41870      * same as applyTemplate, except it's done to one of the subTemplates
41871      * when using named templates, you can do:
41872      *
41873      * var str = pl.applySubTemplate('your-name', values);
41874      *
41875      * 
41876      * @param {Number} id of the template
41877      * @param {Object} values to apply to template
41878      * @param {Object} parent (normaly the instance of this object)
41879      */
41880     applySubTemplate : function(id, values, parent)
41881     {
41882         
41883         
41884         var t = this.tpls[id];
41885         
41886         
41887         try { 
41888             if(t.test && !t.test.call(this, values, parent)){
41889                 return '';
41890             }
41891         } catch(e) {
41892             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
41893             Roo.log(e.toString());
41894             Roo.log(t.test);
41895             return ''
41896         }
41897         try { 
41898             
41899             if(t.exec && t.exec.call(this, values, parent)){
41900                 return '';
41901             }
41902         } catch(e) {
41903             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
41904             Roo.log(e.toString());
41905             Roo.log(t.exec);
41906             return ''
41907         }
41908         try {
41909             var vs = t.target ? t.target.call(this, values, parent) : values;
41910             parent = t.target ? values : parent;
41911             if(t.target && vs instanceof Array){
41912                 var buf = [];
41913                 for(var i = 0, len = vs.length; i < len; i++){
41914                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
41915                 }
41916                 return buf.join('');
41917             }
41918             return t.compiled.call(this, vs, parent);
41919         } catch (e) {
41920             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
41921             Roo.log(e.toString());
41922             Roo.log(t.compiled);
41923             return '';
41924         }
41925     },
41926
41927     compileTpl : function(tpl)
41928     {
41929         var fm = Roo.util.Format;
41930         var useF = this.disableFormats !== true;
41931         var sep = Roo.isGecko ? "+" : ",";
41932         var undef = function(str) {
41933             Roo.log("Property not found :"  + str);
41934             return '';
41935         };
41936         
41937         var fn = function(m, name, format, args)
41938         {
41939             //Roo.log(arguments);
41940             args = args ? args.replace(/\\'/g,"'") : args;
41941             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
41942             if (typeof(format) == 'undefined') {
41943                 format= 'htmlEncode';
41944             }
41945             if (format == 'raw' ) {
41946                 format = false;
41947             }
41948             
41949             if(name.substr(0, 4) == 'xtpl'){
41950                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
41951             }
41952             
41953             // build an array of options to determine if value is undefined..
41954             
41955             // basically get 'xxxx.yyyy' then do
41956             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
41957             //    (function () { Roo.log("Property not found"); return ''; })() :
41958             //    ......
41959             
41960             var udef_ar = [];
41961             var lookfor = '';
41962             Roo.each(name.split('.'), function(st) {
41963                 lookfor += (lookfor.length ? '.': '') + st;
41964                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
41965             });
41966             
41967             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
41968             
41969             
41970             if(format && useF){
41971                 
41972                 args = args ? ',' + args : "";
41973                  
41974                 if(format.substr(0, 5) != "this."){
41975                     format = "fm." + format + '(';
41976                 }else{
41977                     format = 'this.call("'+ format.substr(5) + '", ';
41978                     args = ", values";
41979                 }
41980                 
41981                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
41982             }
41983              
41984             if (args.length) {
41985                 // called with xxyx.yuu:(test,test)
41986                 // change to ()
41987                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
41988             }
41989             // raw.. - :raw modifier..
41990             return "'"+ sep + udef_st  + name + ")"+sep+"'";
41991             
41992         };
41993         var body;
41994         // branched to use + in gecko and [].join() in others
41995         if(Roo.isGecko){
41996             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
41997                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
41998                     "';};};";
41999         }else{
42000             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42001             body.push(tpl.body.replace(/(\r\n|\n)/g,
42002                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42003             body.push("'].join('');};};");
42004             body = body.join('');
42005         }
42006         
42007         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42008        
42009         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42010         eval(body);
42011         
42012         return this;
42013     },
42014
42015     applyTemplate : function(values){
42016         return this.master.compiled.call(this, values, {});
42017         //var s = this.subs;
42018     },
42019
42020     apply : function(){
42021         return this.applyTemplate.apply(this, arguments);
42022     }
42023
42024  });
42025
42026 Roo.XTemplate.from = function(el){
42027     el = Roo.getDom(el);
42028     return new Roo.XTemplate(el.value || el.innerHTML);
42029 };