Roo/Toolbar.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825      
826         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827             //Roo.log('not touch/ button !=0');
828             return;
829         }
830         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831             return; // double touch..
832         }
833         
834
835         if (this.isLocked()) {
836             //Roo.log('locked');
837             return;
838         }
839
840         this.DDM.refreshCache(this.groups);
841 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
844             //Roo.log('no outer handes or not over target');
845                 // do nothing.
846         } else {
847 //            Roo.log('check validator');
848             if (this.clickValidator(e)) {
849 //                Roo.log('validate success');
850                 // set the initial element position
851                 this.setStartPosition();
852
853
854                 this.b4MouseDown(e);
855                 this.onMouseDown(e);
856
857                 this.DDM.handleMouseDown(e, this);
858
859                 this.DDM.stopEvent(e);
860             } else {
861
862
863             }
864         }
865     },
866
867     clickValidator: function(e) {
868         var target = e.getTarget();
869         return ( this.isValidHandleChild(target) &&
870                     (this.id == this.handleElId ||
871                         this.DDM.handleWasClicked(target, this.id)) );
872     },
873
874     /**
875      * Allows you to specify a tag name that should not start a drag operation
876      * when clicked.  This is designed to facilitate embedding links within a
877      * drag handle that do something other than start the drag.
878      * @method addInvalidHandleType
879      * @param {string} tagName the type of element to exclude
880      */
881     addInvalidHandleType: function(tagName) {
882         var type = tagName.toUpperCase();
883         this.invalidHandleTypes[type] = type;
884     },
885
886     /**
887      * Lets you to specify an element id for a child of a drag handle
888      * that should not initiate a drag
889      * @method addInvalidHandleId
890      * @param {string} id the element id of the element you wish to ignore
891      */
892     addInvalidHandleId: function(id) {
893         if (typeof id !== "string") {
894             id = Roo.id(id);
895         }
896         this.invalidHandleIds[id] = id;
897     },
898
899     /**
900      * Lets you specify a css class of elements that will not initiate a drag
901      * @method addInvalidHandleClass
902      * @param {string} cssClass the class of the elements you wish to ignore
903      */
904     addInvalidHandleClass: function(cssClass) {
905         this.invalidHandleClasses.push(cssClass);
906     },
907
908     /**
909      * Unsets an excluded tag name set by addInvalidHandleType
910      * @method removeInvalidHandleType
911      * @param {string} tagName the type of element to unexclude
912      */
913     removeInvalidHandleType: function(tagName) {
914         var type = tagName.toUpperCase();
915         // this.invalidHandleTypes[type] = null;
916         delete this.invalidHandleTypes[type];
917     },
918
919     /**
920      * Unsets an invalid handle id
921      * @method removeInvalidHandleId
922      * @param {string} id the id of the element to re-enable
923      */
924     removeInvalidHandleId: function(id) {
925         if (typeof id !== "string") {
926             id = Roo.id(id);
927         }
928         delete this.invalidHandleIds[id];
929     },
930
931     /**
932      * Unsets an invalid css class
933      * @method removeInvalidHandleClass
934      * @param {string} cssClass the class of the element(s) you wish to
935      * re-enable
936      */
937     removeInvalidHandleClass: function(cssClass) {
938         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939             if (this.invalidHandleClasses[i] == cssClass) {
940                 delete this.invalidHandleClasses[i];
941             }
942         }
943     },
944
945     /**
946      * Checks the tag exclusion list to see if this click should be ignored
947      * @method isValidHandleChild
948      * @param {HTMLElement} node the HTMLElement to evaluate
949      * @return {boolean} true if this is a valid tag type, false if not
950      */
951     isValidHandleChild: function(node) {
952
953         var valid = true;
954         // var n = (node.nodeName == "#text") ? node.parentNode : node;
955         var nodeName;
956         try {
957             nodeName = node.nodeName.toUpperCase();
958         } catch(e) {
959             nodeName = node.nodeName;
960         }
961         valid = valid && !this.invalidHandleTypes[nodeName];
962         valid = valid && !this.invalidHandleIds[node.id];
963
964         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
966         }
967
968
969         return valid;
970
971     },
972
973     /**
974      * Create the array of horizontal tick marks if an interval was specified
975      * in setXConstraint().
976      * @method setXTicks
977      * @private
978      */
979     setXTicks: function(iStartX, iTickSize) {
980         this.xTicks = [];
981         this.xTickSize = iTickSize;
982
983         var tickMap = {};
984
985         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
986             if (!tickMap[i]) {
987                 this.xTicks[this.xTicks.length] = i;
988                 tickMap[i] = true;
989             }
990         }
991
992         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
993             if (!tickMap[i]) {
994                 this.xTicks[this.xTicks.length] = i;
995                 tickMap[i] = true;
996             }
997         }
998
999         this.xTicks.sort(this.DDM.numericSort) ;
1000     },
1001
1002     /**
1003      * Create the array of vertical tick marks if an interval was specified in
1004      * setYConstraint().
1005      * @method setYTicks
1006      * @private
1007      */
1008     setYTicks: function(iStartY, iTickSize) {
1009         this.yTicks = [];
1010         this.yTickSize = iTickSize;
1011
1012         var tickMap = {};
1013
1014         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1015             if (!tickMap[i]) {
1016                 this.yTicks[this.yTicks.length] = i;
1017                 tickMap[i] = true;
1018             }
1019         }
1020
1021         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1022             if (!tickMap[i]) {
1023                 this.yTicks[this.yTicks.length] = i;
1024                 tickMap[i] = true;
1025             }
1026         }
1027
1028         this.yTicks.sort(this.DDM.numericSort) ;
1029     },
1030
1031     /**
1032      * By default, the element can be dragged any place on the screen.  Use
1033      * this method to limit the horizontal travel of the element.  Pass in
1034      * 0,0 for the parameters if you want to lock the drag to the y axis.
1035      * @method setXConstraint
1036      * @param {int} iLeft the number of pixels the element can move to the left
1037      * @param {int} iRight the number of pixels the element can move to the
1038      * right
1039      * @param {int} iTickSize optional parameter for specifying that the
1040      * element
1041      * should move iTickSize pixels at a time.
1042      */
1043     setXConstraint: function(iLeft, iRight, iTickSize) {
1044         this.leftConstraint = iLeft;
1045         this.rightConstraint = iRight;
1046
1047         this.minX = this.initPageX - iLeft;
1048         this.maxX = this.initPageX + iRight;
1049         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1050
1051         this.constrainX = true;
1052     },
1053
1054     /**
1055      * Clears any constraints applied to this instance.  Also clears ticks
1056      * since they can't exist independent of a constraint at this time.
1057      * @method clearConstraints
1058      */
1059     clearConstraints: function() {
1060         this.constrainX = false;
1061         this.constrainY = false;
1062         this.clearTicks();
1063     },
1064
1065     /**
1066      * Clears any tick interval defined for this instance
1067      * @method clearTicks
1068      */
1069     clearTicks: function() {
1070         this.xTicks = null;
1071         this.yTicks = null;
1072         this.xTickSize = 0;
1073         this.yTickSize = 0;
1074     },
1075
1076     /**
1077      * By default, the element can be dragged any place on the screen.  Set
1078      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1079      * parameters if you want to lock the drag to the x axis.
1080      * @method setYConstraint
1081      * @param {int} iUp the number of pixels the element can move up
1082      * @param {int} iDown the number of pixels the element can move down
1083      * @param {int} iTickSize optional parameter for specifying that the
1084      * element should move iTickSize pixels at a time.
1085      */
1086     setYConstraint: function(iUp, iDown, iTickSize) {
1087         this.topConstraint = iUp;
1088         this.bottomConstraint = iDown;
1089
1090         this.minY = this.initPageY - iUp;
1091         this.maxY = this.initPageY + iDown;
1092         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1093
1094         this.constrainY = true;
1095
1096     },
1097
1098     /**
1099      * resetConstraints must be called if you manually reposition a dd element.
1100      * @method resetConstraints
1101      * @param {boolean} maintainOffset
1102      */
1103     resetConstraints: function() {
1104
1105
1106         // Maintain offsets if necessary
1107         if (this.initPageX || this.initPageX === 0) {
1108             // figure out how much this thing has moved
1109             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1111
1112             this.setInitPosition(dx, dy);
1113
1114         // This is the first time we have detected the element's position
1115         } else {
1116             this.setInitPosition();
1117         }
1118
1119         if (this.constrainX) {
1120             this.setXConstraint( this.leftConstraint,
1121                                  this.rightConstraint,
1122                                  this.xTickSize        );
1123         }
1124
1125         if (this.constrainY) {
1126             this.setYConstraint( this.topConstraint,
1127                                  this.bottomConstraint,
1128                                  this.yTickSize         );
1129         }
1130     },
1131
1132     /**
1133      * Normally the drag element is moved pixel by pixel, but we can specify
1134      * that it move a number of pixels at a time.  This method resolves the
1135      * location when we have it set up like this.
1136      * @method getTick
1137      * @param {int} val where we want to place the object
1138      * @param {int[]} tickArray sorted array of valid points
1139      * @return {int} the closest tick
1140      * @private
1141      */
1142     getTick: function(val, tickArray) {
1143
1144         if (!tickArray) {
1145             // If tick interval is not defined, it is effectively 1 pixel,
1146             // so we return the value passed to us.
1147             return val;
1148         } else if (tickArray[0] >= val) {
1149             // The value is lower than the first tick, so we return the first
1150             // tick.
1151             return tickArray[0];
1152         } else {
1153             for (var i=0, len=tickArray.length; i<len; ++i) {
1154                 var next = i + 1;
1155                 if (tickArray[next] && tickArray[next] >= val) {
1156                     var diff1 = val - tickArray[i];
1157                     var diff2 = tickArray[next] - val;
1158                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1159                 }
1160             }
1161
1162             // The value is larger than the last tick, so we return the last
1163             // tick.
1164             return tickArray[tickArray.length - 1];
1165         }
1166     },
1167
1168     /**
1169      * toString method
1170      * @method toString
1171      * @return {string} string representation of the dd obj
1172      */
1173     toString: function() {
1174         return ("DragDrop " + this.id);
1175     }
1176
1177 });
1178
1179 })();
1180 /*
1181  * Based on:
1182  * Ext JS Library 1.1.1
1183  * Copyright(c) 2006-2007, Ext JS, LLC.
1184  *
1185  * Originally Released Under LGPL - original licence link has changed is not relivant.
1186  *
1187  * Fork - LGPL
1188  * <script type="text/javascript">
1189  */
1190
1191
1192 /**
1193  * The drag and drop utility provides a framework for building drag and drop
1194  * applications.  In addition to enabling drag and drop for specific elements,
1195  * the drag and drop elements are tracked by the manager class, and the
1196  * interactions between the various elements are tracked during the drag and
1197  * the implementing code is notified about these important moments.
1198  */
1199
1200 // Only load the library once.  Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1203
1204 /**
1205  * @class Roo.dd.DragDropMgr
1206  * DragDropMgr is a singleton that tracks the element interaction for
1207  * all DragDrop items in the window.  Generally, you will not call
1208  * this class directly, but it does have helper methods that could
1209  * be useful in your DragDrop implementations.
1210  * @singleton
1211  */
1212 Roo.dd.DragDropMgr = function() {
1213
1214     var Event = Roo.EventManager;
1215
1216     return {
1217
1218         /**
1219          * Two dimensional Array of registered DragDrop objects.  The first
1220          * dimension is the DragDrop item group, the second the DragDrop
1221          * object.
1222          * @property ids
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         ids: {},
1228
1229         /**
1230          * Array of element ids defined as drag handles.  Used to determine
1231          * if the element that generated the mousedown event is actually the
1232          * handle and not the html element itself.
1233          * @property handleIds
1234          * @type {string: string}
1235          * @private
1236          * @static
1237          */
1238         handleIds: {},
1239
1240         /**
1241          * the DragDrop object that is currently being dragged
1242          * @property dragCurrent
1243          * @type DragDrop
1244          * @private
1245          * @static
1246          **/
1247         dragCurrent: null,
1248
1249         /**
1250          * the DragDrop object(s) that are being hovered over
1251          * @property dragOvers
1252          * @type Array
1253          * @private
1254          * @static
1255          */
1256         dragOvers: {},
1257
1258         /**
1259          * the X distance between the cursor and the object being dragged
1260          * @property deltaX
1261          * @type int
1262          * @private
1263          * @static
1264          */
1265         deltaX: 0,
1266
1267         /**
1268          * the Y distance between the cursor and the object being dragged
1269          * @property deltaY
1270          * @type int
1271          * @private
1272          * @static
1273          */
1274         deltaY: 0,
1275
1276         /**
1277          * Flag to determine if we should prevent the default behavior of the
1278          * events we define. By default this is true, but this can be set to
1279          * false if you need the default behavior (not recommended)
1280          * @property preventDefault
1281          * @type boolean
1282          * @static
1283          */
1284         preventDefault: true,
1285
1286         /**
1287          * Flag to determine if we should stop the propagation of the events
1288          * we generate. This is true by default but you may want to set it to
1289          * false if the html element contains other features that require the
1290          * mouse click.
1291          * @property stopPropagation
1292          * @type boolean
1293          * @static
1294          */
1295         stopPropagation: true,
1296
1297         /**
1298          * Internal flag that is set to true when drag and drop has been
1299          * intialized
1300          * @property initialized
1301          * @private
1302          * @static
1303          */
1304         initalized: false,
1305
1306         /**
1307          * All drag and drop can be disabled.
1308          * @property locked
1309          * @private
1310          * @static
1311          */
1312         locked: false,
1313
1314         /**
1315          * Called the first time an element is registered.
1316          * @method init
1317          * @private
1318          * @static
1319          */
1320         init: function() {
1321             this.initialized = true;
1322         },
1323
1324         /**
1325          * In point mode, drag and drop interaction is defined by the
1326          * location of the cursor during the drag/drop
1327          * @property POINT
1328          * @type int
1329          * @static
1330          */
1331         POINT: 0,
1332
1333         /**
1334          * In intersect mode, drag and drop interactio nis defined by the
1335          * overlap of two or more drag and drop objects.
1336          * @property INTERSECT
1337          * @type int
1338          * @static
1339          */
1340         INTERSECT: 1,
1341
1342         /**
1343          * The current drag and drop mode.  Default: POINT
1344          * @property mode
1345          * @type int
1346          * @static
1347          */
1348         mode: 0,
1349
1350         /**
1351          * Runs method on all drag and drop objects
1352          * @method _execOnAll
1353          * @private
1354          * @static
1355          */
1356         _execOnAll: function(sMethod, args) {
1357             for (var i in this.ids) {
1358                 for (var j in this.ids[i]) {
1359                     var oDD = this.ids[i][j];
1360                     if (! this.isTypeOfDD(oDD)) {
1361                         continue;
1362                     }
1363                     oDD[sMethod].apply(oDD, args);
1364                 }
1365             }
1366         },
1367
1368         /**
1369          * Drag and drop initialization.  Sets up the global event handlers
1370          * @method _onLoad
1371          * @private
1372          * @static
1373          */
1374         _onLoad: function() {
1375
1376             this.init();
1377
1378             if (!Roo.isTouch) {
1379                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1380                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1381             }
1382             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1383             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1384             
1385             Event.on(window,   "unload",    this._onUnload, this, true);
1386             Event.on(window,   "resize",    this._onResize, this, true);
1387             // Event.on(window,   "mouseout",    this._test);
1388
1389         },
1390
1391         /**
1392          * Reset constraints on all drag and drop objs
1393          * @method _onResize
1394          * @private
1395          * @static
1396          */
1397         _onResize: function(e) {
1398             this._execOnAll("resetConstraints", []);
1399         },
1400
1401         /**
1402          * Lock all drag and drop functionality
1403          * @method lock
1404          * @static
1405          */
1406         lock: function() { this.locked = true; },
1407
1408         /**
1409          * Unlock all drag and drop functionality
1410          * @method unlock
1411          * @static
1412          */
1413         unlock: function() { this.locked = false; },
1414
1415         /**
1416          * Is drag and drop locked?
1417          * @method isLocked
1418          * @return {boolean} True if drag and drop is locked, false otherwise.
1419          * @static
1420          */
1421         isLocked: function() { return this.locked; },
1422
1423         /**
1424          * Location cache that is set for all drag drop objects when a drag is
1425          * initiated, cleared when the drag is finished.
1426          * @property locationCache
1427          * @private
1428          * @static
1429          */
1430         locationCache: {},
1431
1432         /**
1433          * Set useCache to false if you want to force object the lookup of each
1434          * drag and drop linked element constantly during a drag.
1435          * @property useCache
1436          * @type boolean
1437          * @static
1438          */
1439         useCache: true,
1440
1441         /**
1442          * The number of pixels that the mouse needs to move after the
1443          * mousedown before the drag is initiated.  Default=3;
1444          * @property clickPixelThresh
1445          * @type int
1446          * @static
1447          */
1448         clickPixelThresh: 3,
1449
1450         /**
1451          * The number of milliseconds after the mousedown event to initiate the
1452          * drag if we don't get a mouseup event. Default=1000
1453          * @property clickTimeThresh
1454          * @type int
1455          * @static
1456          */
1457         clickTimeThresh: 350,
1458
1459         /**
1460          * Flag that indicates that either the drag pixel threshold or the
1461          * mousdown time threshold has been met
1462          * @property dragThreshMet
1463          * @type boolean
1464          * @private
1465          * @static
1466          */
1467         dragThreshMet: false,
1468
1469         /**
1470          * Timeout used for the click time threshold
1471          * @property clickTimeout
1472          * @type Object
1473          * @private
1474          * @static
1475          */
1476         clickTimeout: null,
1477
1478         /**
1479          * The X position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startX
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startX: 0,
1487
1488         /**
1489          * The Y position of the mousedown event stored for later use when a
1490          * drag threshold is met.
1491          * @property startY
1492          * @type int
1493          * @private
1494          * @static
1495          */
1496         startY: 0,
1497
1498         /**
1499          * Each DragDrop instance must be registered with the DragDropMgr.
1500          * This is executed in DragDrop.init()
1501          * @method regDragDrop
1502          * @param {DragDrop} oDD the DragDrop object to register
1503          * @param {String} sGroup the name of the group this element belongs to
1504          * @static
1505          */
1506         regDragDrop: function(oDD, sGroup) {
1507             if (!this.initialized) { this.init(); }
1508
1509             if (!this.ids[sGroup]) {
1510                 this.ids[sGroup] = {};
1511             }
1512             this.ids[sGroup][oDD.id] = oDD;
1513         },
1514
1515         /**
1516          * Removes the supplied dd instance from the supplied group. Executed
1517          * by DragDrop.removeFromGroup, so don't call this function directly.
1518          * @method removeDDFromGroup
1519          * @private
1520          * @static
1521          */
1522         removeDDFromGroup: function(oDD, sGroup) {
1523             if (!this.ids[sGroup]) {
1524                 this.ids[sGroup] = {};
1525             }
1526
1527             var obj = this.ids[sGroup];
1528             if (obj && obj[oDD.id]) {
1529                 delete obj[oDD.id];
1530             }
1531         },
1532
1533         /**
1534          * Unregisters a drag and drop item.  This is executed in
1535          * DragDrop.unreg, use that method instead of calling this directly.
1536          * @method _remove
1537          * @private
1538          * @static
1539          */
1540         _remove: function(oDD) {
1541             for (var g in oDD.groups) {
1542                 if (g && this.ids[g][oDD.id]) {
1543                     delete this.ids[g][oDD.id];
1544                 }
1545             }
1546             delete this.handleIds[oDD.id];
1547         },
1548
1549         /**
1550          * Each DragDrop handle element must be registered.  This is done
1551          * automatically when executing DragDrop.setHandleElId()
1552          * @method regHandle
1553          * @param {String} sDDId the DragDrop id this element is a handle for
1554          * @param {String} sHandleId the id of the element that is the drag
1555          * handle
1556          * @static
1557          */
1558         regHandle: function(sDDId, sHandleId) {
1559             if (!this.handleIds[sDDId]) {
1560                 this.handleIds[sDDId] = {};
1561             }
1562             this.handleIds[sDDId][sHandleId] = sHandleId;
1563         },
1564
1565         /**
1566          * Utility function to determine if a given element has been
1567          * registered as a drag drop item.
1568          * @method isDragDrop
1569          * @param {String} id the element id to check
1570          * @return {boolean} true if this element is a DragDrop item,
1571          * false otherwise
1572          * @static
1573          */
1574         isDragDrop: function(id) {
1575             return ( this.getDDById(id) ) ? true : false;
1576         },
1577
1578         /**
1579          * Returns the drag and drop instances that are in all groups the
1580          * passed in instance belongs to.
1581          * @method getRelated
1582          * @param {DragDrop} p_oDD the obj to get related data for
1583          * @param {boolean} bTargetsOnly if true, only return targetable objs
1584          * @return {DragDrop[]} the related instances
1585          * @static
1586          */
1587         getRelated: function(p_oDD, bTargetsOnly) {
1588             var oDDs = [];
1589             for (var i in p_oDD.groups) {
1590                 for (j in this.ids[i]) {
1591                     var dd = this.ids[i][j];
1592                     if (! this.isTypeOfDD(dd)) {
1593                         continue;
1594                     }
1595                     if (!bTargetsOnly || dd.isTarget) {
1596                         oDDs[oDDs.length] = dd;
1597                     }
1598                 }
1599             }
1600
1601             return oDDs;
1602         },
1603
1604         /**
1605          * Returns true if the specified dd target is a legal target for
1606          * the specifice drag obj
1607          * @method isLegalTarget
1608          * @param {DragDrop} the drag obj
1609          * @param {DragDrop} the target
1610          * @return {boolean} true if the target is a legal target for the
1611          * dd obj
1612          * @static
1613          */
1614         isLegalTarget: function (oDD, oTargetDD) {
1615             var targets = this.getRelated(oDD, true);
1616             for (var i=0, len=targets.length;i<len;++i) {
1617                 if (targets[i].id == oTargetDD.id) {
1618                     return true;
1619                 }
1620             }
1621
1622             return false;
1623         },
1624
1625         /**
1626          * My goal is to be able to transparently determine if an object is
1627          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1628          * returns "object", oDD.constructor.toString() always returns
1629          * "DragDrop" and not the name of the subclass.  So for now it just
1630          * evaluates a well-known variable in DragDrop.
1631          * @method isTypeOfDD
1632          * @param {Object} the object to evaluate
1633          * @return {boolean} true if typeof oDD = DragDrop
1634          * @static
1635          */
1636         isTypeOfDD: function (oDD) {
1637             return (oDD && oDD.__ygDragDrop);
1638         },
1639
1640         /**
1641          * Utility function to determine if a given element has been
1642          * registered as a drag drop handle for the given Drag Drop object.
1643          * @method isHandle
1644          * @param {String} id the element id to check
1645          * @return {boolean} true if this element is a DragDrop handle, false
1646          * otherwise
1647          * @static
1648          */
1649         isHandle: function(sDDId, sHandleId) {
1650             return ( this.handleIds[sDDId] &&
1651                             this.handleIds[sDDId][sHandleId] );
1652         },
1653
1654         /**
1655          * Returns the DragDrop instance for a given id
1656          * @method getDDById
1657          * @param {String} id the id of the DragDrop object
1658          * @return {DragDrop} the drag drop object, null if it is not found
1659          * @static
1660          */
1661         getDDById: function(id) {
1662             for (var i in this.ids) {
1663                 if (this.ids[i][id]) {
1664                     return this.ids[i][id];
1665                 }
1666             }
1667             return null;
1668         },
1669
1670         /**
1671          * Fired after a registered DragDrop object gets the mousedown event.
1672          * Sets up the events required to track the object being dragged
1673          * @method handleMouseDown
1674          * @param {Event} e the event
1675          * @param oDD the DragDrop object being dragged
1676          * @private
1677          * @static
1678          */
1679         handleMouseDown: function(e, oDD) {
1680             if(Roo.QuickTips){
1681                 Roo.QuickTips.disable();
1682             }
1683             this.currentTarget = e.getTarget();
1684
1685             this.dragCurrent = oDD;
1686
1687             var el = oDD.getEl();
1688
1689             // track start position
1690             this.startX = e.getPageX();
1691             this.startY = e.getPageY();
1692
1693             this.deltaX = this.startX - el.offsetLeft;
1694             this.deltaY = this.startY - el.offsetTop;
1695
1696             this.dragThreshMet = false;
1697
1698             this.clickTimeout = setTimeout(
1699                     function() {
1700                         var DDM = Roo.dd.DDM;
1701                         DDM.startDrag(DDM.startX, DDM.startY);
1702                     },
1703                     this.clickTimeThresh );
1704         },
1705
1706         /**
1707          * Fired when either the drag pixel threshol or the mousedown hold
1708          * time threshold has been met.
1709          * @method startDrag
1710          * @param x {int} the X position of the original mousedown
1711          * @param y {int} the Y position of the original mousedown
1712          * @static
1713          */
1714         startDrag: function(x, y) {
1715             clearTimeout(this.clickTimeout);
1716             if (this.dragCurrent) {
1717                 this.dragCurrent.b4StartDrag(x, y);
1718                 this.dragCurrent.startDrag(x, y);
1719             }
1720             this.dragThreshMet = true;
1721         },
1722
1723         /**
1724          * Internal function to handle the mouseup event.  Will be invoked
1725          * from the context of the document.
1726          * @method handleMouseUp
1727          * @param {Event} e the event
1728          * @private
1729          * @static
1730          */
1731         handleMouseUp: function(e) {
1732
1733             if(Roo.QuickTips){
1734                 Roo.QuickTips.enable();
1735             }
1736             if (! this.dragCurrent) {
1737                 return;
1738             }
1739
1740             clearTimeout(this.clickTimeout);
1741
1742             if (this.dragThreshMet) {
1743                 this.fireEvents(e, true);
1744             } else {
1745             }
1746
1747             this.stopDrag(e);
1748
1749             this.stopEvent(e);
1750         },
1751
1752         /**
1753          * Utility to stop event propagation and event default, if these
1754          * features are turned on.
1755          * @method stopEvent
1756          * @param {Event} e the event as returned by this.getEvent()
1757          * @static
1758          */
1759         stopEvent: function(e){
1760             if(this.stopPropagation) {
1761                 e.stopPropagation();
1762             }
1763
1764             if (this.preventDefault) {
1765                 e.preventDefault();
1766             }
1767         },
1768
1769         /**
1770          * Internal function to clean up event handlers after the drag
1771          * operation is complete
1772          * @method stopDrag
1773          * @param {Event} e the event
1774          * @private
1775          * @static
1776          */
1777         stopDrag: function(e) {
1778             // Fire the drag end event for the item that was dragged
1779             if (this.dragCurrent) {
1780                 if (this.dragThreshMet) {
1781                     this.dragCurrent.b4EndDrag(e);
1782                     this.dragCurrent.endDrag(e);
1783                 }
1784
1785                 this.dragCurrent.onMouseUp(e);
1786             }
1787
1788             this.dragCurrent = null;
1789             this.dragOvers = {};
1790         },
1791
1792         /**
1793          * Internal function to handle the mousemove event.  Will be invoked
1794          * from the context of the html element.
1795          *
1796          * @TODO figure out what we can do about mouse events lost when the
1797          * user drags objects beyond the window boundary.  Currently we can
1798          * detect this in internet explorer by verifying that the mouse is
1799          * down during the mousemove event.  Firefox doesn't give us the
1800          * button state on the mousemove event.
1801          * @method handleMouseMove
1802          * @param {Event} e the event
1803          * @private
1804          * @static
1805          */
1806         handleMouseMove: function(e) {
1807             if (! this.dragCurrent) {
1808                 return true;
1809             }
1810
1811             // var button = e.which || e.button;
1812
1813             // check for IE mouseup outside of page boundary
1814             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1815                 this.stopEvent(e);
1816                 return this.handleMouseUp(e);
1817             }
1818
1819             if (!this.dragThreshMet) {
1820                 var diffX = Math.abs(this.startX - e.getPageX());
1821                 var diffY = Math.abs(this.startY - e.getPageY());
1822                 if (diffX > this.clickPixelThresh ||
1823                             diffY > this.clickPixelThresh) {
1824                     this.startDrag(this.startX, this.startY);
1825                 }
1826             }
1827
1828             if (this.dragThreshMet) {
1829                 this.dragCurrent.b4Drag(e);
1830                 this.dragCurrent.onDrag(e);
1831                 if(!this.dragCurrent.moveOnly){
1832                     this.fireEvents(e, false);
1833                 }
1834             }
1835
1836             this.stopEvent(e);
1837
1838             return true;
1839         },
1840
1841         /**
1842          * Iterates over all of the DragDrop elements to find ones we are
1843          * hovering over or dropping on
1844          * @method fireEvents
1845          * @param {Event} e the event
1846          * @param {boolean} isDrop is this a drop op or a mouseover op?
1847          * @private
1848          * @static
1849          */
1850         fireEvents: function(e, isDrop) {
1851             var dc = this.dragCurrent;
1852
1853             // If the user did the mouse up outside of the window, we could
1854             // get here even though we have ended the drag.
1855             if (!dc || dc.isLocked()) {
1856                 return;
1857             }
1858
1859             var pt = e.getPoint();
1860
1861             // cache the previous dragOver array
1862             var oldOvers = [];
1863
1864             var outEvts   = [];
1865             var overEvts  = [];
1866             var dropEvts  = [];
1867             var enterEvts = [];
1868
1869             // Check to see if the object(s) we were hovering over is no longer
1870             // being hovered over so we can fire the onDragOut event
1871             for (var i in this.dragOvers) {
1872
1873                 var ddo = this.dragOvers[i];
1874
1875                 if (! this.isTypeOfDD(ddo)) {
1876                     continue;
1877                 }
1878
1879                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880                     outEvts.push( ddo );
1881                 }
1882
1883                 oldOvers[i] = true;
1884                 delete this.dragOvers[i];
1885             }
1886
1887             for (var sGroup in dc.groups) {
1888
1889                 if ("string" != typeof sGroup) {
1890                     continue;
1891                 }
1892
1893                 for (i in this.ids[sGroup]) {
1894                     var oDD = this.ids[sGroup][i];
1895                     if (! this.isTypeOfDD(oDD)) {
1896                         continue;
1897                     }
1898
1899                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900                         if (this.isOverTarget(pt, oDD, this.mode)) {
1901                             // look for drop interactions
1902                             if (isDrop) {
1903                                 dropEvts.push( oDD );
1904                             // look for drag enter and drag over interactions
1905                             } else {
1906
1907                                 // initial drag over: dragEnter fires
1908                                 if (!oldOvers[oDD.id]) {
1909                                     enterEvts.push( oDD );
1910                                 // subsequent drag overs: dragOver fires
1911                                 } else {
1912                                     overEvts.push( oDD );
1913                                 }
1914
1915                                 this.dragOvers[oDD.id] = oDD;
1916                             }
1917                         }
1918                     }
1919                 }
1920             }
1921
1922             if (this.mode) {
1923                 if (outEvts.length) {
1924                     dc.b4DragOut(e, outEvts);
1925                     dc.onDragOut(e, outEvts);
1926                 }
1927
1928                 if (enterEvts.length) {
1929                     dc.onDragEnter(e, enterEvts);
1930                 }
1931
1932                 if (overEvts.length) {
1933                     dc.b4DragOver(e, overEvts);
1934                     dc.onDragOver(e, overEvts);
1935                 }
1936
1937                 if (dropEvts.length) {
1938                     dc.b4DragDrop(e, dropEvts);
1939                     dc.onDragDrop(e, dropEvts);
1940                 }
1941
1942             } else {
1943                 // fire dragout events
1944                 var len = 0;
1945                 for (i=0, len=outEvts.length; i<len; ++i) {
1946                     dc.b4DragOut(e, outEvts[i].id);
1947                     dc.onDragOut(e, outEvts[i].id);
1948                 }
1949
1950                 // fire enter events
1951                 for (i=0,len=enterEvts.length; i<len; ++i) {
1952                     // dc.b4DragEnter(e, oDD.id);
1953                     dc.onDragEnter(e, enterEvts[i].id);
1954                 }
1955
1956                 // fire over events
1957                 for (i=0,len=overEvts.length; i<len; ++i) {
1958                     dc.b4DragOver(e, overEvts[i].id);
1959                     dc.onDragOver(e, overEvts[i].id);
1960                 }
1961
1962                 // fire drop events
1963                 for (i=0, len=dropEvts.length; i<len; ++i) {
1964                     dc.b4DragDrop(e, dropEvts[i].id);
1965                     dc.onDragDrop(e, dropEvts[i].id);
1966                 }
1967
1968             }
1969
1970             // notify about a drop that did not find a target
1971             if (isDrop && !dropEvts.length) {
1972                 dc.onInvalidDrop(e);
1973             }
1974
1975         },
1976
1977         /**
1978          * Helper function for getting the best match from the list of drag
1979          * and drop objects returned by the drag and drop events when we are
1980          * in INTERSECT mode.  It returns either the first object that the
1981          * cursor is over, or the object that has the greatest overlap with
1982          * the dragged element.
1983          * @method getBestMatch
1984          * @param  {DragDrop[]} dds The array of drag and drop objects
1985          * targeted
1986          * @return {DragDrop}       The best single match
1987          * @static
1988          */
1989         getBestMatch: function(dds) {
1990             var winner = null;
1991             // Return null if the input is not what we expect
1992             //if (!dds || !dds.length || dds.length == 0) {
1993                // winner = null;
1994             // If there is only one item, it wins
1995             //} else if (dds.length == 1) {
1996
1997             var len = dds.length;
1998
1999             if (len == 1) {
2000                 winner = dds[0];
2001             } else {
2002                 // Loop through the targeted items
2003                 for (var i=0; i<len; ++i) {
2004                     var dd = dds[i];
2005                     // If the cursor is over the object, it wins.  If the
2006                     // cursor is over multiple matches, the first one we come
2007                     // to wins.
2008                     if (dd.cursorIsOver) {
2009                         winner = dd;
2010                         break;
2011                     // Otherwise the object with the most overlap wins
2012                     } else {
2013                         if (!winner ||
2014                             winner.overlap.getArea() < dd.overlap.getArea()) {
2015                             winner = dd;
2016                         }
2017                     }
2018                 }
2019             }
2020
2021             return winner;
2022         },
2023
2024         /**
2025          * Refreshes the cache of the top-left and bottom-right points of the
2026          * drag and drop objects in the specified group(s).  This is in the
2027          * format that is stored in the drag and drop instance, so typical
2028          * usage is:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2031          * </code>
2032          * Alternatively:
2033          * <code>
2034          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2035          * </code>
2036          * @TODO this really should be an indexed array.  Alternatively this
2037          * method could accept both.
2038          * @method refreshCache
2039          * @param {Object} groups an associative array of groups to refresh
2040          * @static
2041          */
2042         refreshCache: function(groups) {
2043             for (var sGroup in groups) {
2044                 if ("string" != typeof sGroup) {
2045                     continue;
2046                 }
2047                 for (var i in this.ids[sGroup]) {
2048                     var oDD = this.ids[sGroup][i];
2049
2050                     if (this.isTypeOfDD(oDD)) {
2051                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052                         var loc = this.getLocation(oDD);
2053                         if (loc) {
2054                             this.locationCache[oDD.id] = loc;
2055                         } else {
2056                             delete this.locationCache[oDD.id];
2057                             // this will unregister the drag and drop object if
2058                             // the element is not in a usable state
2059                             // oDD.unreg();
2060                         }
2061                     }
2062                 }
2063             }
2064         },
2065
2066         /**
2067          * This checks to make sure an element exists and is in the DOM.  The
2068          * main purpose is to handle cases where innerHTML is used to remove
2069          * drag and drop objects from the DOM.  IE provides an 'unspecified
2070          * error' when trying to access the offsetParent of such an element
2071          * @method verifyEl
2072          * @param {HTMLElement} el the element to check
2073          * @return {boolean} true if the element looks usable
2074          * @static
2075          */
2076         verifyEl: function(el) {
2077             if (el) {
2078                 var parent;
2079                 if(Roo.isIE){
2080                     try{
2081                         parent = el.offsetParent;
2082                     }catch(e){}
2083                 }else{
2084                     parent = el.offsetParent;
2085                 }
2086                 if (parent) {
2087                     return true;
2088                 }
2089             }
2090
2091             return false;
2092         },
2093
2094         /**
2095          * Returns a Region object containing the drag and drop element's position
2096          * and size, including the padding configured for it
2097          * @method getLocation
2098          * @param {DragDrop} oDD the drag and drop object to get the
2099          *                       location for
2100          * @return {Roo.lib.Region} a Region object representing the total area
2101          *                             the element occupies, including any padding
2102          *                             the instance is configured for.
2103          * @static
2104          */
2105         getLocation: function(oDD) {
2106             if (! this.isTypeOfDD(oDD)) {
2107                 return null;
2108             }
2109
2110             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2111
2112             try {
2113                 pos= Roo.lib.Dom.getXY(el);
2114             } catch (e) { }
2115
2116             if (!pos) {
2117                 return null;
2118             }
2119
2120             x1 = pos[0];
2121             x2 = x1 + el.offsetWidth;
2122             y1 = pos[1];
2123             y2 = y1 + el.offsetHeight;
2124
2125             t = y1 - oDD.padding[0];
2126             r = x2 + oDD.padding[1];
2127             b = y2 + oDD.padding[2];
2128             l = x1 - oDD.padding[3];
2129
2130             return new Roo.lib.Region( t, r, b, l );
2131         },
2132
2133         /**
2134          * Checks the cursor location to see if it over the target
2135          * @method isOverTarget
2136          * @param {Roo.lib.Point} pt The point to evaluate
2137          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138          * @return {boolean} true if the mouse is over the target
2139          * @private
2140          * @static
2141          */
2142         isOverTarget: function(pt, oTarget, intersect) {
2143             // use cache if available
2144             var loc = this.locationCache[oTarget.id];
2145             if (!loc || !this.useCache) {
2146                 loc = this.getLocation(oTarget);
2147                 this.locationCache[oTarget.id] = loc;
2148
2149             }
2150
2151             if (!loc) {
2152                 return false;
2153             }
2154
2155             oTarget.cursorIsOver = loc.contains( pt );
2156
2157             // DragDrop is using this as a sanity check for the initial mousedown
2158             // in this case we are done.  In POINT mode, if the drag obj has no
2159             // contraints, we are also done. Otherwise we need to evaluate the
2160             // location of the target as related to the actual location of the
2161             // dragged element.
2162             var dc = this.dragCurrent;
2163             if (!dc || !dc.getTargetCoord ||
2164                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2165                 return oTarget.cursorIsOver;
2166             }
2167
2168             oTarget.overlap = null;
2169
2170             // Get the current location of the drag element, this is the
2171             // location of the mouse event less the delta that represents
2172             // where the original mousedown happened on the element.  We
2173             // need to consider constraints and ticks as well.
2174             var pos = dc.getTargetCoord(pt.x, pt.y);
2175
2176             var el = dc.getDragEl();
2177             var curRegion = new Roo.lib.Region( pos.y,
2178                                                    pos.x + el.offsetWidth,
2179                                                    pos.y + el.offsetHeight,
2180                                                    pos.x );
2181
2182             var overlap = curRegion.intersect(loc);
2183
2184             if (overlap) {
2185                 oTarget.overlap = overlap;
2186                 return (intersect) ? true : oTarget.cursorIsOver;
2187             } else {
2188                 return false;
2189             }
2190         },
2191
2192         /**
2193          * unload event handler
2194          * @method _onUnload
2195          * @private
2196          * @static
2197          */
2198         _onUnload: function(e, me) {
2199             Roo.dd.DragDropMgr.unregAll();
2200         },
2201
2202         /**
2203          * Cleans up the drag and drop events and objects.
2204          * @method unregAll
2205          * @private
2206          * @static
2207          */
2208         unregAll: function() {
2209
2210             if (this.dragCurrent) {
2211                 this.stopDrag();
2212                 this.dragCurrent = null;
2213             }
2214
2215             this._execOnAll("unreg", []);
2216
2217             for (i in this.elementCache) {
2218                 delete this.elementCache[i];
2219             }
2220
2221             this.elementCache = {};
2222             this.ids = {};
2223         },
2224
2225         /**
2226          * A cache of DOM elements
2227          * @property elementCache
2228          * @private
2229          * @static
2230          */
2231         elementCache: {},
2232
2233         /**
2234          * Get the wrapper for the DOM element specified
2235          * @method getElWrapper
2236          * @param {String} id the id of the element to get
2237          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2238          * @private
2239          * @deprecated This wrapper isn't that useful
2240          * @static
2241          */
2242         getElWrapper: function(id) {
2243             var oWrapper = this.elementCache[id];
2244             if (!oWrapper || !oWrapper.el) {
2245                 oWrapper = this.elementCache[id] =
2246                     new this.ElementWrapper(Roo.getDom(id));
2247             }
2248             return oWrapper;
2249         },
2250
2251         /**
2252          * Returns the actual DOM element
2253          * @method getElement
2254          * @param {String} id the id of the elment to get
2255          * @return {Object} The element
2256          * @deprecated use Roo.getDom instead
2257          * @static
2258          */
2259         getElement: function(id) {
2260             return Roo.getDom(id);
2261         },
2262
2263         /**
2264          * Returns the style property for the DOM element (i.e.,
2265          * document.getElById(id).style)
2266          * @method getCss
2267          * @param {String} id the id of the elment to get
2268          * @return {Object} The style property of the element
2269          * @deprecated use Roo.getDom instead
2270          * @static
2271          */
2272         getCss: function(id) {
2273             var el = Roo.getDom(id);
2274             return (el) ? el.style : null;
2275         },
2276
2277         /**
2278          * Inner class for cached elements
2279          * @class DragDropMgr.ElementWrapper
2280          * @for DragDropMgr
2281          * @private
2282          * @deprecated
2283          */
2284         ElementWrapper: function(el) {
2285                 /**
2286                  * The element
2287                  * @property el
2288                  */
2289                 this.el = el || null;
2290                 /**
2291                  * The element id
2292                  * @property id
2293                  */
2294                 this.id = this.el && el.id;
2295                 /**
2296                  * A reference to the style property
2297                  * @property css
2298                  */
2299                 this.css = this.el && el.style;
2300             },
2301
2302         /**
2303          * Returns the X position of an html element
2304          * @method getPosX
2305          * @param el the element for which to get the position
2306          * @return {int} the X coordinate
2307          * @for DragDropMgr
2308          * @deprecated use Roo.lib.Dom.getX instead
2309          * @static
2310          */
2311         getPosX: function(el) {
2312             return Roo.lib.Dom.getX(el);
2313         },
2314
2315         /**
2316          * Returns the Y position of an html element
2317          * @method getPosY
2318          * @param el the element for which to get the position
2319          * @return {int} the Y coordinate
2320          * @deprecated use Roo.lib.Dom.getY instead
2321          * @static
2322          */
2323         getPosY: function(el) {
2324             return Roo.lib.Dom.getY(el);
2325         },
2326
2327         /**
2328          * Swap two nodes.  In IE, we use the native method, for others we
2329          * emulate the IE behavior
2330          * @method swapNode
2331          * @param n1 the first node to swap
2332          * @param n2 the other node to swap
2333          * @static
2334          */
2335         swapNode: function(n1, n2) {
2336             if (n1.swapNode) {
2337                 n1.swapNode(n2);
2338             } else {
2339                 var p = n2.parentNode;
2340                 var s = n2.nextSibling;
2341
2342                 if (s == n1) {
2343                     p.insertBefore(n1, n2);
2344                 } else if (n2 == n1.nextSibling) {
2345                     p.insertBefore(n2, n1);
2346                 } else {
2347                     n1.parentNode.replaceChild(n2, n1);
2348                     p.insertBefore(n1, s);
2349                 }
2350             }
2351         },
2352
2353         /**
2354          * Returns the current scroll position
2355          * @method getScroll
2356          * @private
2357          * @static
2358          */
2359         getScroll: function () {
2360             var t, l, dde=document.documentElement, db=document.body;
2361             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2362                 t = dde.scrollTop;
2363                 l = dde.scrollLeft;
2364             } else if (db) {
2365                 t = db.scrollTop;
2366                 l = db.scrollLeft;
2367             } else {
2368
2369             }
2370             return { top: t, left: l };
2371         },
2372
2373         /**
2374          * Returns the specified element style property
2375          * @method getStyle
2376          * @param {HTMLElement} el          the element
2377          * @param {string}      styleProp   the style property
2378          * @return {string} The value of the style property
2379          * @deprecated use Roo.lib.Dom.getStyle
2380          * @static
2381          */
2382         getStyle: function(el, styleProp) {
2383             return Roo.fly(el).getStyle(styleProp);
2384         },
2385
2386         /**
2387          * Gets the scrollTop
2388          * @method getScrollTop
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollTop: function () { return this.getScroll().top; },
2393
2394         /**
2395          * Gets the scrollLeft
2396          * @method getScrollLeft
2397          * @return {int} the document's scrollTop
2398          * @static
2399          */
2400         getScrollLeft: function () { return this.getScroll().left; },
2401
2402         /**
2403          * Sets the x/y position of an element to the location of the
2404          * target element.
2405          * @method moveToEl
2406          * @param {HTMLElement} moveEl      The element to move
2407          * @param {HTMLElement} targetEl    The position reference element
2408          * @static
2409          */
2410         moveToEl: function (moveEl, targetEl) {
2411             var aCoord = Roo.lib.Dom.getXY(targetEl);
2412             Roo.lib.Dom.setXY(moveEl, aCoord);
2413         },
2414
2415         /**
2416          * Numeric array sort function
2417          * @method numericSort
2418          * @static
2419          */
2420         numericSort: function(a, b) { return (a - b); },
2421
2422         /**
2423          * Internal counter
2424          * @property _timeoutCount
2425          * @private
2426          * @static
2427          */
2428         _timeoutCount: 0,
2429
2430         /**
2431          * Trying to make the load order less important.  Without this we get
2432          * an error if this file is loaded before the Event Utility.
2433          * @method _addListeners
2434          * @private
2435          * @static
2436          */
2437         _addListeners: function() {
2438             var DDM = Roo.dd.DDM;
2439             if ( Roo.lib.Event && document ) {
2440                 DDM._onLoad();
2441             } else {
2442                 if (DDM._timeoutCount > 2000) {
2443                 } else {
2444                     setTimeout(DDM._addListeners, 10);
2445                     if (document && document.body) {
2446                         DDM._timeoutCount += 1;
2447                     }
2448                 }
2449             }
2450         },
2451
2452         /**
2453          * Recursively searches the immediate parent and all child nodes for
2454          * the handle element in order to determine wheter or not it was
2455          * clicked.
2456          * @method handleWasClicked
2457          * @param node the html element to inspect
2458          * @static
2459          */
2460         handleWasClicked: function(node, id) {
2461             if (this.isHandle(id, node.id)) {
2462                 return true;
2463             } else {
2464                 // check to see if this is a text node child of the one we want
2465                 var p = node.parentNode;
2466
2467                 while (p) {
2468                     if (this.isHandle(id, p.id)) {
2469                         return true;
2470                     } else {
2471                         p = p.parentNode;
2472                     }
2473                 }
2474             }
2475
2476             return false;
2477         }
2478
2479     };
2480
2481 }();
2482
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2486
2487 }/*
2488  * Based on:
2489  * Ext JS Library 1.1.1
2490  * Copyright(c) 2006-2007, Ext JS, LLC.
2491  *
2492  * Originally Released Under LGPL - original licence link has changed is not relivant.
2493  *
2494  * Fork - LGPL
2495  * <script type="text/javascript">
2496  */
2497
2498 /**
2499  * @class Roo.dd.DD
2500  * A DragDrop implementation where the linked element follows the
2501  * mouse cursor during a drag.
2502  * @extends Roo.dd.DragDrop
2503  * @constructor
2504  * @param {String} id the id of the linked element
2505  * @param {String} sGroup the group of related DragDrop items
2506  * @param {object} config an object containing configurable attributes
2507  *                Valid properties for DD:
2508  *                    scroll
2509  */
2510 Roo.dd.DD = function(id, sGroup, config) {
2511     if (id) {
2512         this.init(id, sGroup, config);
2513     }
2514 };
2515
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2517
2518     /**
2519      * When set to true, the utility automatically tries to scroll the browser
2520      * window wehn a drag and drop element is dragged near the viewport boundary.
2521      * Defaults to true.
2522      * @property scroll
2523      * @type boolean
2524      */
2525     scroll: true,
2526
2527     /**
2528      * Sets the pointer offset to the distance between the linked element's top
2529      * left corner and the location the element was clicked
2530      * @method autoOffset
2531      * @param {int} iPageX the X coordinate of the click
2532      * @param {int} iPageY the Y coordinate of the click
2533      */
2534     autoOffset: function(iPageX, iPageY) {
2535         var x = iPageX - this.startPageX;
2536         var y = iPageY - this.startPageY;
2537         this.setDelta(x, y);
2538     },
2539
2540     /**
2541      * Sets the pointer offset.  You can call this directly to force the
2542      * offset to be in a particular location (e.g., pass in 0,0 to set it
2543      * to the center of the object)
2544      * @method setDelta
2545      * @param {int} iDeltaX the distance from the left
2546      * @param {int} iDeltaY the distance from the top
2547      */
2548     setDelta: function(iDeltaX, iDeltaY) {
2549         this.deltaX = iDeltaX;
2550         this.deltaY = iDeltaY;
2551     },
2552
2553     /**
2554      * Sets the drag element to the location of the mousedown or click event,
2555      * maintaining the cursor location relative to the location on the element
2556      * that was clicked.  Override this if you want to place the element in a
2557      * location other than where the cursor is.
2558      * @method setDragElPos
2559      * @param {int} iPageX the X coordinate of the mousedown or drag event
2560      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561      */
2562     setDragElPos: function(iPageX, iPageY) {
2563         // the first time we do this, we are going to check to make sure
2564         // the element has css positioning
2565
2566         var el = this.getDragEl();
2567         this.alignElWithMouse(el, iPageX, iPageY);
2568     },
2569
2570     /**
2571      * Sets the element to the location of the mousedown or click event,
2572      * maintaining the cursor location relative to the location on the element
2573      * that was clicked.  Override this if you want to place the element in a
2574      * location other than where the cursor is.
2575      * @method alignElWithMouse
2576      * @param {HTMLElement} el the element to move
2577      * @param {int} iPageX the X coordinate of the mousedown or drag event
2578      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2579      */
2580     alignElWithMouse: function(el, iPageX, iPageY) {
2581         var oCoord = this.getTargetCoord(iPageX, iPageY);
2582         var fly = el.dom ? el : Roo.fly(el);
2583         if (!this.deltaSetXY) {
2584             var aCoord = [oCoord.x, oCoord.y];
2585             fly.setXY(aCoord);
2586             var newLeft = fly.getLeft(true);
2587             var newTop  = fly.getTop(true);
2588             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2589         } else {
2590             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2591         }
2592
2593         this.cachePosition(oCoord.x, oCoord.y);
2594         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595         return oCoord;
2596     },
2597
2598     /**
2599      * Saves the most recent position so that we can reset the constraints and
2600      * tick marks on-demand.  We need to know this so that we can calculate the
2601      * number of pixels the element is offset from its original position.
2602      * @method cachePosition
2603      * @param iPageX the current x position (optional, this just makes it so we
2604      * don't have to look it up again)
2605      * @param iPageY the current y position (optional, this just makes it so we
2606      * don't have to look it up again)
2607      */
2608     cachePosition: function(iPageX, iPageY) {
2609         if (iPageX) {
2610             this.lastPageX = iPageX;
2611             this.lastPageY = iPageY;
2612         } else {
2613             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614             this.lastPageX = aCoord[0];
2615             this.lastPageY = aCoord[1];
2616         }
2617     },
2618
2619     /**
2620      * Auto-scroll the window if the dragged object has been moved beyond the
2621      * visible window boundary.
2622      * @method autoScroll
2623      * @param {int} x the drag element's x position
2624      * @param {int} y the drag element's y position
2625      * @param {int} h the height of the drag element
2626      * @param {int} w the width of the drag element
2627      * @private
2628      */
2629     autoScroll: function(x, y, h, w) {
2630
2631         if (this.scroll) {
2632             // The client height
2633             var clientH = Roo.lib.Dom.getViewWidth();
2634
2635             // The client width
2636             var clientW = Roo.lib.Dom.getViewHeight();
2637
2638             // The amt scrolled down
2639             var st = this.DDM.getScrollTop();
2640
2641             // The amt scrolled right
2642             var sl = this.DDM.getScrollLeft();
2643
2644             // Location of the bottom of the element
2645             var bot = h + y;
2646
2647             // Location of the right of the element
2648             var right = w + x;
2649
2650             // The distance from the cursor to the bottom of the visible area,
2651             // adjusted so that we don't scroll if the cursor is beyond the
2652             // element drag constraints
2653             var toBot = (clientH + st - y - this.deltaY);
2654
2655             // The distance from the cursor to the right of the visible area
2656             var toRight = (clientW + sl - x - this.deltaX);
2657
2658
2659             // How close to the edge the cursor must be before we scroll
2660             // var thresh = (document.all) ? 100 : 40;
2661             var thresh = 40;
2662
2663             // How many pixels to scroll per autoscroll op.  This helps to reduce
2664             // clunky scrolling. IE is more sensitive about this ... it needs this
2665             // value to be higher.
2666             var scrAmt = (document.all) ? 80 : 30;
2667
2668             // Scroll down if we are near the bottom of the visible page and the
2669             // obj extends below the crease
2670             if ( bot > clientH && toBot < thresh ) {
2671                 window.scrollTo(sl, st + scrAmt);
2672             }
2673
2674             // Scroll up if the window is scrolled down and the top of the object
2675             // goes above the top border
2676             if ( y < st && st > 0 && y - st < thresh ) {
2677                 window.scrollTo(sl, st - scrAmt);
2678             }
2679
2680             // Scroll right if the obj is beyond the right border and the cursor is
2681             // near the border.
2682             if ( right > clientW && toRight < thresh ) {
2683                 window.scrollTo(sl + scrAmt, st);
2684             }
2685
2686             // Scroll left if the window has been scrolled to the right and the obj
2687             // extends past the left border
2688             if ( x < sl && sl > 0 && x - sl < thresh ) {
2689                 window.scrollTo(sl - scrAmt, st);
2690             }
2691         }
2692     },
2693
2694     /**
2695      * Finds the location the element should be placed if we want to move
2696      * it to where the mouse location less the click offset would place us.
2697      * @method getTargetCoord
2698      * @param {int} iPageX the X coordinate of the click
2699      * @param {int} iPageY the Y coordinate of the click
2700      * @return an object that contains the coordinates (Object.x and Object.y)
2701      * @private
2702      */
2703     getTargetCoord: function(iPageX, iPageY) {
2704
2705
2706         var x = iPageX - this.deltaX;
2707         var y = iPageY - this.deltaY;
2708
2709         if (this.constrainX) {
2710             if (x < this.minX) { x = this.minX; }
2711             if (x > this.maxX) { x = this.maxX; }
2712         }
2713
2714         if (this.constrainY) {
2715             if (y < this.minY) { y = this.minY; }
2716             if (y > this.maxY) { y = this.maxY; }
2717         }
2718
2719         x = this.getTick(x, this.xTicks);
2720         y = this.getTick(y, this.yTicks);
2721
2722
2723         return {x:x, y:y};
2724     },
2725
2726     /*
2727      * Sets up config options specific to this class. Overrides
2728      * Roo.dd.DragDrop, but all versions of this method through the
2729      * inheritance chain are called
2730      */
2731     applyConfig: function() {
2732         Roo.dd.DD.superclass.applyConfig.call(this);
2733         this.scroll = (this.config.scroll !== false);
2734     },
2735
2736     /*
2737      * Event that fires prior to the onMouseDown event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4MouseDown: function(e) {
2741         // this.resetConstraints();
2742         this.autoOffset(e.getPageX(),
2743                             e.getPageY());
2744     },
2745
2746     /*
2747      * Event that fires prior to the onDrag event.  Overrides
2748      * Roo.dd.DragDrop.
2749      */
2750     b4Drag: function(e) {
2751         this.setDragElPos(e.getPageX(),
2752                             e.getPageY());
2753     },
2754
2755     toString: function() {
2756         return ("DD " + this.id);
2757     }
2758
2759     //////////////////////////////////////////////////////////////////////////
2760     // Debugging ygDragDrop events that can be overridden
2761     //////////////////////////////////////////////////////////////////////////
2762     /*
2763     startDrag: function(x, y) {
2764     },
2765
2766     onDrag: function(e) {
2767     },
2768
2769     onDragEnter: function(e, id) {
2770     },
2771
2772     onDragOver: function(e, id) {
2773     },
2774
2775     onDragOut: function(e, id) {
2776     },
2777
2778     onDragDrop: function(e, id) {
2779     },
2780
2781     endDrag: function(e) {
2782     }
2783
2784     */
2785
2786 });/*
2787  * Based on:
2788  * Ext JS Library 1.1.1
2789  * Copyright(c) 2006-2007, Ext JS, LLC.
2790  *
2791  * Originally Released Under LGPL - original licence link has changed is not relivant.
2792  *
2793  * Fork - LGPL
2794  * <script type="text/javascript">
2795  */
2796
2797 /**
2798  * @class Roo.dd.DDProxy
2799  * A DragDrop implementation that inserts an empty, bordered div into
2800  * the document that follows the cursor during drag operations.  At the time of
2801  * the click, the frame div is resized to the dimensions of the linked html
2802  * element, and moved to the exact location of the linked element.
2803  *
2804  * References to the "frame" element refer to the single proxy element that
2805  * was created to be dragged in place of all DDProxy elements on the
2806  * page.
2807  *
2808  * @extends Roo.dd.DD
2809  * @constructor
2810  * @param {String} id the id of the linked html element
2811  * @param {String} sGroup the group of related DragDrop objects
2812  * @param {object} config an object containing configurable attributes
2813  *                Valid properties for DDProxy in addition to those in DragDrop:
2814  *                   resizeFrame, centerFrame, dragElId
2815  */
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2817     if (id) {
2818         this.init(id, sGroup, config);
2819         this.initFrame();
2820     }
2821 };
2822
2823 /**
2824  * The default drag frame div id
2825  * @property Roo.dd.DDProxy.dragElId
2826  * @type String
2827  * @static
2828  */
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2830
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2832
2833     /**
2834      * By default we resize the drag frame to be the same size as the element
2835      * we want to drag (this is to get the frame effect).  We can turn it off
2836      * if we want a different behavior.
2837      * @property resizeFrame
2838      * @type boolean
2839      */
2840     resizeFrame: true,
2841
2842     /**
2843      * By default the frame is positioned exactly where the drag element is, so
2844      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2845      * you do not have constraints on the obj is to have the drag frame centered
2846      * around the cursor.  Set centerFrame to true for this effect.
2847      * @property centerFrame
2848      * @type boolean
2849      */
2850     centerFrame: false,
2851
2852     /**
2853      * Creates the proxy element if it does not yet exist
2854      * @method createFrame
2855      */
2856     createFrame: function() {
2857         var self = this;
2858         var body = document.body;
2859
2860         if (!body || !body.firstChild) {
2861             setTimeout( function() { self.createFrame(); }, 50 );
2862             return;
2863         }
2864
2865         var div = this.getDragEl();
2866
2867         if (!div) {
2868             div    = document.createElement("div");
2869             div.id = this.dragElId;
2870             var s  = div.style;
2871
2872             s.position   = "absolute";
2873             s.visibility = "hidden";
2874             s.cursor     = "move";
2875             s.border     = "2px solid #aaa";
2876             s.zIndex     = 999;
2877
2878             // appendChild can blow up IE if invoked prior to the window load event
2879             // while rendering a table.  It is possible there are other scenarios
2880             // that would cause this to happen as well.
2881             body.insertBefore(div, body.firstChild);
2882         }
2883     },
2884
2885     /**
2886      * Initialization for the drag frame element.  Must be called in the
2887      * constructor of all subclasses
2888      * @method initFrame
2889      */
2890     initFrame: function() {
2891         this.createFrame();
2892     },
2893
2894     applyConfig: function() {
2895         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2896
2897         this.resizeFrame = (this.config.resizeFrame !== false);
2898         this.centerFrame = (this.config.centerFrame);
2899         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2900     },
2901
2902     /**
2903      * Resizes the drag frame to the dimensions of the clicked object, positions
2904      * it over the object, and finally displays it
2905      * @method showFrame
2906      * @param {int} iPageX X click position
2907      * @param {int} iPageY Y click position
2908      * @private
2909      */
2910     showFrame: function(iPageX, iPageY) {
2911         var el = this.getEl();
2912         var dragEl = this.getDragEl();
2913         var s = dragEl.style;
2914
2915         this._resizeProxy();
2916
2917         if (this.centerFrame) {
2918             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2919                            Math.round(parseInt(s.height, 10)/2) );
2920         }
2921
2922         this.setDragElPos(iPageX, iPageY);
2923
2924         Roo.fly(dragEl).show();
2925     },
2926
2927     /**
2928      * The proxy is automatically resized to the dimensions of the linked
2929      * element when a drag is initiated, unless resizeFrame is set to false
2930      * @method _resizeProxy
2931      * @private
2932      */
2933     _resizeProxy: function() {
2934         if (this.resizeFrame) {
2935             var el = this.getEl();
2936             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2937         }
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4MouseDown: function(e) {
2942         var x = e.getPageX();
2943         var y = e.getPageY();
2944         this.autoOffset(x, y);
2945         this.setDragElPos(x, y);
2946     },
2947
2948     // overrides Roo.dd.DragDrop
2949     b4StartDrag: function(x, y) {
2950         // show the drag frame
2951         this.showFrame(x, y);
2952     },
2953
2954     // overrides Roo.dd.DragDrop
2955     b4EndDrag: function(e) {
2956         Roo.fly(this.getDragEl()).hide();
2957     },
2958
2959     // overrides Roo.dd.DragDrop
2960     // By default we try to move the element to the last location of the frame.
2961     // This is so that the default behavior mirrors that of Roo.dd.DD.
2962     endDrag: function(e) {
2963
2964         var lel = this.getEl();
2965         var del = this.getDragEl();
2966
2967         // Show the drag frame briefly so we can get its position
2968         del.style.visibility = "";
2969
2970         this.beforeMove();
2971         // Hide the linked element before the move to get around a Safari
2972         // rendering bug.
2973         lel.style.visibility = "hidden";
2974         Roo.dd.DDM.moveToEl(lel, del);
2975         del.style.visibility = "hidden";
2976         lel.style.visibility = "";
2977
2978         this.afterDrag();
2979     },
2980
2981     beforeMove : function(){
2982
2983     },
2984
2985     afterDrag : function(){
2986
2987     },
2988
2989     toString: function() {
2990         return ("DDProxy " + this.id);
2991     }
2992
2993 });
2994 /*
2995  * Based on:
2996  * Ext JS Library 1.1.1
2997  * Copyright(c) 2006-2007, Ext JS, LLC.
2998  *
2999  * Originally Released Under LGPL - original licence link has changed is not relivant.
3000  *
3001  * Fork - LGPL
3002  * <script type="text/javascript">
3003  */
3004
3005  /**
3006  * @class Roo.dd.DDTarget
3007  * A DragDrop implementation that does not move, but can be a drop
3008  * target.  You would get the same result by simply omitting implementation
3009  * for the event callbacks, but this way we reduce the processing cost of the
3010  * event listener and the callbacks.
3011  * @extends Roo.dd.DragDrop
3012  * @constructor
3013  * @param {String} id the id of the element that is a drop target
3014  * @param {String} sGroup the group of related DragDrop objects
3015  * @param {object} config an object containing configurable attributes
3016  *                 Valid properties for DDTarget in addition to those in
3017  *                 DragDrop:
3018  *                    none
3019  */
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3021     if (id) {
3022         this.initTarget(id, sGroup, config);
3023     }
3024     if (config.listeners || config.events) { 
3025        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3026             listeners : config.listeners || {}, 
3027             events : config.events || {} 
3028         });    
3029     }
3030 };
3031
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034     toString: function() {
3035         return ("DDTarget " + this.id);
3036     }
3037 });
3038 /*
3039  * Based on:
3040  * Ext JS Library 1.1.1
3041  * Copyright(c) 2006-2007, Ext JS, LLC.
3042  *
3043  * Originally Released Under LGPL - original licence link has changed is not relivant.
3044  *
3045  * Fork - LGPL
3046  * <script type="text/javascript">
3047  */
3048  
3049
3050 /**
3051  * @class Roo.dd.ScrollManager
3052  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3054  * @singleton
3055  */
3056 Roo.dd.ScrollManager = function(){
3057     var ddm = Roo.dd.DragDropMgr;
3058     var els = {};
3059     var dragEl = null;
3060     var proc = {};
3061     
3062     
3063     
3064     var onStop = function(e){
3065         dragEl = null;
3066         clearProc();
3067     };
3068     
3069     var triggerRefresh = function(){
3070         if(ddm.dragCurrent){
3071              ddm.refreshCache(ddm.dragCurrent.groups);
3072         }
3073     };
3074     
3075     var doScroll = function(){
3076         if(ddm.dragCurrent){
3077             var dds = Roo.dd.ScrollManager;
3078             if(!dds.animate){
3079                 if(proc.el.scroll(proc.dir, dds.increment)){
3080                     triggerRefresh();
3081                 }
3082             }else{
3083                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084             }
3085         }
3086     };
3087     
3088     var clearProc = function(){
3089         if(proc.id){
3090             clearInterval(proc.id);
3091         }
3092         proc.id = 0;
3093         proc.el = null;
3094         proc.dir = "";
3095     };
3096     
3097     var startProc = function(el, dir){
3098          Roo.log('scroll startproc');
3099         clearProc();
3100         proc.el = el;
3101         proc.dir = dir;
3102         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3103     };
3104     
3105     var onFire = function(e, isDrop){
3106        
3107         if(isDrop || !ddm.dragCurrent){ return; }
3108         var dds = Roo.dd.ScrollManager;
3109         if(!dragEl || dragEl != ddm.dragCurrent){
3110             dragEl = ddm.dragCurrent;
3111             // refresh regions on drag start
3112             dds.refreshCache();
3113         }
3114         
3115         var xy = Roo.lib.Event.getXY(e);
3116         var pt = new Roo.lib.Point(xy[0], xy[1]);
3117         for(var id in els){
3118             var el = els[id], r = el._region;
3119             if(r && r.contains(pt) && el.isScrollable()){
3120                 if(r.bottom - pt.y <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "down");
3123                     }
3124                     return;
3125                 }else if(r.right - pt.x <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "left");
3128                     }
3129                     return;
3130                 }else if(pt.y - r.top <= dds.thresh){
3131                     if(proc.el != el){
3132                         startProc(el, "up");
3133                     }
3134                     return;
3135                 }else if(pt.x - r.left <= dds.thresh){
3136                     if(proc.el != el){
3137                         startProc(el, "right");
3138                     }
3139                     return;
3140                 }
3141             }
3142         }
3143         clearProc();
3144     };
3145     
3146     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3148     
3149     return {
3150         /**
3151          * Registers new overflow element(s) to auto scroll
3152          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3153          */
3154         register : function(el){
3155             if(el instanceof Array){
3156                 for(var i = 0, len = el.length; i < len; i++) {
3157                         this.register(el[i]);
3158                 }
3159             }else{
3160                 el = Roo.get(el);
3161                 els[el.id] = el;
3162             }
3163             Roo.dd.ScrollManager.els = els;
3164         },
3165         
3166         /**
3167          * Unregisters overflow element(s) so they are no longer scrolled
3168          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3169          */
3170         unregister : function(el){
3171             if(el instanceof Array){
3172                 for(var i = 0, len = el.length; i < len; i++) {
3173                         this.unregister(el[i]);
3174                 }
3175             }else{
3176                 el = Roo.get(el);
3177                 delete els[el.id];
3178             }
3179         },
3180         
3181         /**
3182          * The number of pixels from the edge of a container the pointer needs to be to 
3183          * trigger scrolling (defaults to 25)
3184          * @type Number
3185          */
3186         thresh : 25,
3187         
3188         /**
3189          * The number of pixels to scroll in each scroll increment (defaults to 50)
3190          * @type Number
3191          */
3192         increment : 100,
3193         
3194         /**
3195          * The frequency of scrolls in milliseconds (defaults to 500)
3196          * @type Number
3197          */
3198         frequency : 500,
3199         
3200         /**
3201          * True to animate the scroll (defaults to true)
3202          * @type Boolean
3203          */
3204         animate: true,
3205         
3206         /**
3207          * The animation duration in seconds - 
3208          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3209          * @type Number
3210          */
3211         animDuration: .4,
3212         
3213         /**
3214          * Manually trigger a cache refresh.
3215          */
3216         refreshCache : function(){
3217             for(var id in els){
3218                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219                     els[id]._region = els[id].getRegion();
3220                 }
3221             }
3222         }
3223     };
3224 }();/*
3225  * Based on:
3226  * Ext JS Library 1.1.1
3227  * Copyright(c) 2006-2007, Ext JS, LLC.
3228  *
3229  * Originally Released Under LGPL - original licence link has changed is not relivant.
3230  *
3231  * Fork - LGPL
3232  * <script type="text/javascript">
3233  */
3234  
3235
3236 /**
3237  * @class Roo.dd.Registry
3238  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3239  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3240  * @singleton
3241  */
3242 Roo.dd.Registry = function(){
3243     var elements = {}; 
3244     var handles = {}; 
3245     var autoIdSeed = 0;
3246
3247     var getId = function(el, autogen){
3248         if(typeof el == "string"){
3249             return el;
3250         }
3251         var id = el.id;
3252         if(!id && autogen !== false){
3253             id = "roodd-" + (++autoIdSeed);
3254             el.id = id;
3255         }
3256         return id;
3257     };
3258     
3259     return {
3260     /**
3261      * Register a drag drop element
3262      * @param {String|HTMLElement} element The id or DOM node to register
3263      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3265      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266      * populated in the data object (if applicable):
3267      * <pre>
3268 Value      Description<br />
3269 ---------  ------------------------------------------<br />
3270 handles    Array of DOM nodes that trigger dragging<br />
3271            for the element being registered<br />
3272 isHandle   True if the element passed in triggers<br />
3273            dragging itself, else false
3274 </pre>
3275      */
3276         register : function(el, data){
3277             data = data || {};
3278             if(typeof el == "string"){
3279                 el = document.getElementById(el);
3280             }
3281             data.ddel = el;
3282             elements[getId(el)] = data;
3283             if(data.isHandle !== false){
3284                 handles[data.ddel.id] = data;
3285             }
3286             if(data.handles){
3287                 var hs = data.handles;
3288                 for(var i = 0, len = hs.length; i < len; i++){
3289                         handles[getId(hs[i])] = data;
3290                 }
3291             }
3292         },
3293
3294     /**
3295      * Unregister a drag drop element
3296      * @param {String|HTMLElement}  element The id or DOM node to unregister
3297      */
3298         unregister : function(el){
3299             var id = getId(el, false);
3300             var data = elements[id];
3301             if(data){
3302                 delete elements[id];
3303                 if(data.handles){
3304                     var hs = data.handles;
3305                     for(var i = 0, len = hs.length; i < len; i++){
3306                         delete handles[getId(hs[i], false)];
3307                     }
3308                 }
3309             }
3310         },
3311
3312     /**
3313      * Returns the handle registered for a DOM Node by id
3314      * @param {String|HTMLElement} id The DOM node or id to look up
3315      * @return {Object} handle The custom handle data
3316      */
3317         getHandle : function(id){
3318             if(typeof id != "string"){ // must be element?
3319                 id = id.id;
3320             }
3321             return handles[id];
3322         },
3323
3324     /**
3325      * Returns the handle that is registered for the DOM node that is the target of the event
3326      * @param {Event} e The event
3327      * @return {Object} handle The custom handle data
3328      */
3329         getHandleFromEvent : function(e){
3330             var t = Roo.lib.Event.getTarget(e);
3331             return t ? handles[t.id] : null;
3332         },
3333
3334     /**
3335      * Returns a custom data object that is registered for a DOM node by id
3336      * @param {String|HTMLElement} id The DOM node or id to look up
3337      * @return {Object} data The custom data
3338      */
3339         getTarget : function(id){
3340             if(typeof id != "string"){ // must be element?
3341                 id = id.id;
3342             }
3343             return elements[id];
3344         },
3345
3346     /**
3347      * Returns a custom data object that is registered for the DOM node that is the target of the event
3348      * @param {Event} e The event
3349      * @return {Object} data The custom data
3350      */
3351         getTargetFromEvent : function(e){
3352             var t = Roo.lib.Event.getTarget(e);
3353             return t ? elements[t.id] || handles[t.id] : null;
3354         }
3355     };
3356 }();/*
3357  * Based on:
3358  * Ext JS Library 1.1.1
3359  * Copyright(c) 2006-2007, Ext JS, LLC.
3360  *
3361  * Originally Released Under LGPL - original licence link has changed is not relivant.
3362  *
3363  * Fork - LGPL
3364  * <script type="text/javascript">
3365  */
3366  
3367
3368 /**
3369  * @class Roo.dd.StatusProxy
3370  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3371  * default drag proxy used by all Roo.dd components.
3372  * @constructor
3373  * @param {Object} config
3374  */
3375 Roo.dd.StatusProxy = function(config){
3376     Roo.apply(this, config);
3377     this.id = this.id || Roo.id();
3378     this.el = new Roo.Layer({
3379         dh: {
3380             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381                 {tag: "div", cls: "x-dd-drop-icon"},
3382                 {tag: "div", cls: "x-dd-drag-ghost"}
3383             ]
3384         }, 
3385         shadow: !config || config.shadow !== false
3386     });
3387     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388     this.dropStatus = this.dropNotAllowed;
3389 };
3390
3391 Roo.dd.StatusProxy.prototype = {
3392     /**
3393      * @cfg {String} dropAllowed
3394      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3395      */
3396     dropAllowed : "x-dd-drop-ok",
3397     /**
3398      * @cfg {String} dropNotAllowed
3399      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3400      */
3401     dropNotAllowed : "x-dd-drop-nodrop",
3402
3403     /**
3404      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405      * over the current target element.
3406      * @param {String} cssClass The css class for the new drop status indicator image
3407      */
3408     setStatus : function(cssClass){
3409         cssClass = cssClass || this.dropNotAllowed;
3410         if(this.dropStatus != cssClass){
3411             this.el.replaceClass(this.dropStatus, cssClass);
3412             this.dropStatus = cssClass;
3413         }
3414     },
3415
3416     /**
3417      * Resets the status indicator to the default dropNotAllowed value
3418      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3419      */
3420     reset : function(clearGhost){
3421         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422         this.dropStatus = this.dropNotAllowed;
3423         if(clearGhost){
3424             this.ghost.update("");
3425         }
3426     },
3427
3428     /**
3429      * Updates the contents of the ghost element
3430      * @param {String} html The html that will replace the current innerHTML of the ghost element
3431      */
3432     update : function(html){
3433         if(typeof html == "string"){
3434             this.ghost.update(html);
3435         }else{
3436             this.ghost.update("");
3437             html.style.margin = "0";
3438             this.ghost.dom.appendChild(html);
3439         }
3440         // ensure float = none set?? cant remember why though.
3441         var el = this.ghost.dom.firstChild;
3442                 if(el){
3443                         Roo.fly(el).setStyle('float', 'none');
3444                 }
3445     },
3446     
3447     /**
3448      * Returns the underlying proxy {@link Roo.Layer}
3449      * @return {Roo.Layer} el
3450     */
3451     getEl : function(){
3452         return this.el;
3453     },
3454
3455     /**
3456      * Returns the ghost element
3457      * @return {Roo.Element} el
3458      */
3459     getGhost : function(){
3460         return this.ghost;
3461     },
3462
3463     /**
3464      * Hides the proxy
3465      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3466      */
3467     hide : function(clear){
3468         this.el.hide();
3469         if(clear){
3470             this.reset(true);
3471         }
3472     },
3473
3474     /**
3475      * Stops the repair animation if it's currently running
3476      */
3477     stop : function(){
3478         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3479             this.anim.stop();
3480         }
3481     },
3482
3483     /**
3484      * Displays this proxy
3485      */
3486     show : function(){
3487         this.el.show();
3488     },
3489
3490     /**
3491      * Force the Layer to sync its shadow and shim positions to the element
3492      */
3493     sync : function(){
3494         this.el.sync();
3495     },
3496
3497     /**
3498      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3499      * invalid drop operation by the item being dragged.
3500      * @param {Array} xy The XY position of the element ([x, y])
3501      * @param {Function} callback The function to call after the repair is complete
3502      * @param {Object} scope The scope in which to execute the callback
3503      */
3504     repair : function(xy, callback, scope){
3505         this.callback = callback;
3506         this.scope = scope;
3507         if(xy && this.animRepair !== false){
3508             this.el.addClass("x-dd-drag-repair");
3509             this.el.hideUnders(true);
3510             this.anim = this.el.shift({
3511                 duration: this.repairDuration || .5,
3512                 easing: 'easeOut',
3513                 xy: xy,
3514                 stopFx: true,
3515                 callback: this.afterRepair,
3516                 scope: this
3517             });
3518         }else{
3519             this.afterRepair();
3520         }
3521     },
3522
3523     // private
3524     afterRepair : function(){
3525         this.hide(true);
3526         if(typeof this.callback == "function"){
3527             this.callback.call(this.scope || this);
3528         }
3529         this.callback = null;
3530         this.scope = null;
3531     }
3532 };/*
3533  * Based on:
3534  * Ext JS Library 1.1.1
3535  * Copyright(c) 2006-2007, Ext JS, LLC.
3536  *
3537  * Originally Released Under LGPL - original licence link has changed is not relivant.
3538  *
3539  * Fork - LGPL
3540  * <script type="text/javascript">
3541  */
3542
3543 /**
3544  * @class Roo.dd.DragSource
3545  * @extends Roo.dd.DDProxy
3546  * A simple class that provides the basic implementation needed to make any element draggable.
3547  * @constructor
3548  * @param {String/HTMLElement/Element} el The container element
3549  * @param {Object} config
3550  */
3551 Roo.dd.DragSource = function(el, config){
3552     this.el = Roo.get(el);
3553     this.dragData = {};
3554     
3555     Roo.apply(this, config);
3556     
3557     if(!this.proxy){
3558         this.proxy = new Roo.dd.StatusProxy();
3559     }
3560
3561     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3563     
3564     this.dragging = false;
3565 };
3566
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3568     /**
3569      * @cfg {String} dropAllowed
3570      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3571      */
3572     dropAllowed : "x-dd-drop-ok",
3573     /**
3574      * @cfg {String} dropNotAllowed
3575      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3576      */
3577     dropNotAllowed : "x-dd-drop-nodrop",
3578
3579     /**
3580      * Returns the data object associated with this drag source
3581      * @return {Object} data An object containing arbitrary data
3582      */
3583     getDragData : function(e){
3584         return this.dragData;
3585     },
3586
3587     // private
3588     onDragEnter : function(e, id){
3589         var target = Roo.dd.DragDropMgr.getDDById(id);
3590         this.cachedTarget = target;
3591         if(this.beforeDragEnter(target, e, id) !== false){
3592             if(target.isNotifyTarget){
3593                 var status = target.notifyEnter(this, e, this.dragData);
3594                 this.proxy.setStatus(status);
3595             }else{
3596                 this.proxy.setStatus(this.dropAllowed);
3597             }
3598             
3599             if(this.afterDragEnter){
3600                 /**
3601                  * An empty function by default, but provided so that you can perform a custom action
3602                  * when the dragged item enters the drop target by providing an implementation.
3603                  * @param {Roo.dd.DragDrop} target The drop target
3604                  * @param {Event} e The event object
3605                  * @param {String} id The id of the dragged element
3606                  * @method afterDragEnter
3607                  */
3608                 this.afterDragEnter(target, e, id);
3609             }
3610         }
3611     },
3612
3613     /**
3614      * An empty function by default, but provided so that you can perform a custom action
3615      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616      * @param {Roo.dd.DragDrop} target The drop target
3617      * @param {Event} e The event object
3618      * @param {String} id The id of the dragged element
3619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3620      */
3621     beforeDragEnter : function(target, e, id){
3622         return true;
3623     },
3624
3625     // private
3626     alignElWithMouse: function() {
3627         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628         this.proxy.sync();
3629     },
3630
3631     // private
3632     onDragOver : function(e, id){
3633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634         if(this.beforeDragOver(target, e, id) !== false){
3635             if(target.isNotifyTarget){
3636                 var status = target.notifyOver(this, e, this.dragData);
3637                 this.proxy.setStatus(status);
3638             }
3639
3640             if(this.afterDragOver){
3641                 /**
3642                  * An empty function by default, but provided so that you can perform a custom action
3643                  * while the dragged item is over the drop target by providing an implementation.
3644                  * @param {Roo.dd.DragDrop} target The drop target
3645                  * @param {Event} e The event object
3646                  * @param {String} id The id of the dragged element
3647                  * @method afterDragOver
3648                  */
3649                 this.afterDragOver(target, e, id);
3650             }
3651         }
3652     },
3653
3654     /**
3655      * An empty function by default, but provided so that you can perform a custom action
3656      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657      * @param {Roo.dd.DragDrop} target The drop target
3658      * @param {Event} e The event object
3659      * @param {String} id The id of the dragged element
3660      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3661      */
3662     beforeDragOver : function(target, e, id){
3663         return true;
3664     },
3665
3666     // private
3667     onDragOut : function(e, id){
3668         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669         if(this.beforeDragOut(target, e, id) !== false){
3670             if(target.isNotifyTarget){
3671                 target.notifyOut(this, e, this.dragData);
3672             }
3673             this.proxy.reset();
3674             if(this.afterDragOut){
3675                 /**
3676                  * An empty function by default, but provided so that you can perform a custom action
3677                  * after the dragged item is dragged out of the target without dropping.
3678                  * @param {Roo.dd.DragDrop} target The drop target
3679                  * @param {Event} e The event object
3680                  * @param {String} id The id of the dragged element
3681                  * @method afterDragOut
3682                  */
3683                 this.afterDragOut(target, e, id);
3684             }
3685         }
3686         this.cachedTarget = null;
3687     },
3688
3689     /**
3690      * An empty function by default, but provided so that you can perform a custom action before the dragged
3691      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692      * @param {Roo.dd.DragDrop} target The drop target
3693      * @param {Event} e The event object
3694      * @param {String} id The id of the dragged element
3695      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3696      */
3697     beforeDragOut : function(target, e, id){
3698         return true;
3699     },
3700     
3701     // private
3702     onDragDrop : function(e, id){
3703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704         if(this.beforeDragDrop(target, e, id) !== false){
3705             if(target.isNotifyTarget){
3706                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707                     this.onValidDrop(target, e, id);
3708                 }else{
3709                     this.onInvalidDrop(target, e, id);
3710                 }
3711             }else{
3712                 this.onValidDrop(target, e, id);
3713             }
3714             
3715             if(this.afterDragDrop){
3716                 /**
3717                  * An empty function by default, but provided so that you can perform a custom action
3718                  * after a valid drag drop has occurred by providing an implementation.
3719                  * @param {Roo.dd.DragDrop} target The drop target
3720                  * @param {Event} e The event object
3721                  * @param {String} id The id of the dropped element
3722                  * @method afterDragDrop
3723                  */
3724                 this.afterDragDrop(target, e, id);
3725             }
3726         }
3727         delete this.cachedTarget;
3728     },
3729
3730     /**
3731      * An empty function by default, but provided so that you can perform a custom action before the dragged
3732      * item is dropped onto the target and optionally cancel the onDragDrop.
3733      * @param {Roo.dd.DragDrop} target The drop target
3734      * @param {Event} e The event object
3735      * @param {String} id The id of the dragged element
3736      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3737      */
3738     beforeDragDrop : function(target, e, id){
3739         return true;
3740     },
3741
3742     // private
3743     onValidDrop : function(target, e, id){
3744         this.hideProxy();
3745         if(this.afterValidDrop){
3746             /**
3747              * An empty function by default, but provided so that you can perform a custom action
3748              * after a valid drop has occurred by providing an implementation.
3749              * @param {Object} target The target DD 
3750              * @param {Event} e The event object
3751              * @param {String} id The id of the dropped element
3752              * @method afterInvalidDrop
3753              */
3754             this.afterValidDrop(target, e, id);
3755         }
3756     },
3757
3758     // private
3759     getRepairXY : function(e, data){
3760         return this.el.getXY();  
3761     },
3762
3763     // private
3764     onInvalidDrop : function(target, e, id){
3765         this.beforeInvalidDrop(target, e, id);
3766         if(this.cachedTarget){
3767             if(this.cachedTarget.isNotifyTarget){
3768                 this.cachedTarget.notifyOut(this, e, this.dragData);
3769             }
3770             this.cacheTarget = null;
3771         }
3772         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3773
3774         if(this.afterInvalidDrop){
3775             /**
3776              * An empty function by default, but provided so that you can perform a custom action
3777              * after an invalid drop has occurred by providing an implementation.
3778              * @param {Event} e The event object
3779              * @param {String} id The id of the dropped element
3780              * @method afterInvalidDrop
3781              */
3782             this.afterInvalidDrop(e, id);
3783         }
3784     },
3785
3786     // private
3787     afterRepair : function(){
3788         if(Roo.enableFx){
3789             this.el.highlight(this.hlColor || "c3daf9");
3790         }
3791         this.dragging = false;
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action after an invalid
3796      * drop has occurred.
3797      * @param {Roo.dd.DragDrop} target The drop target
3798      * @param {Event} e The event object
3799      * @param {String} id The id of the dragged element
3800      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3801      */
3802     beforeInvalidDrop : function(target, e, id){
3803         return true;
3804     },
3805
3806     // private
3807     handleMouseDown : function(e){
3808         if(this.dragging) {
3809             return;
3810         }
3811         var data = this.getDragData(e);
3812         if(data && this.onBeforeDrag(data, e) !== false){
3813             this.dragData = data;
3814             this.proxy.stop();
3815             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816         } 
3817     },
3818
3819     /**
3820      * An empty function by default, but provided so that you can perform a custom action before the initial
3821      * drag event begins and optionally cancel it.
3822      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823      * @param {Event} e The event object
3824      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3825      */
3826     onBeforeDrag : function(data, e){
3827         return true;
3828     },
3829
3830     /**
3831      * An empty function by default, but provided so that you can perform a custom action once the initial
3832      * drag event has begun.  The drag cannot be canceled from this function.
3833      * @param {Number} x The x position of the click on the dragged object
3834      * @param {Number} y The y position of the click on the dragged object
3835      */
3836     onStartDrag : Roo.emptyFn,
3837
3838     // private - YUI override
3839     startDrag : function(x, y){
3840         this.proxy.reset();
3841         this.dragging = true;
3842         this.proxy.update("");
3843         this.onInitDrag(x, y);
3844         this.proxy.show();
3845     },
3846
3847     // private
3848     onInitDrag : function(x, y){
3849         var clone = this.el.dom.cloneNode(true);
3850         clone.id = Roo.id(); // prevent duplicate ids
3851         this.proxy.update(clone);
3852         this.onStartDrag(x, y);
3853         return true;
3854     },
3855
3856     /**
3857      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3859      */
3860     getProxy : function(){
3861         return this.proxy;  
3862     },
3863
3864     /**
3865      * Hides the drag source's {@link Roo.dd.StatusProxy}
3866      */
3867     hideProxy : function(){
3868         this.proxy.hide();  
3869         this.proxy.reset(true);
3870         this.dragging = false;
3871     },
3872
3873     // private
3874     triggerCacheRefresh : function(){
3875         Roo.dd.DDM.refreshCache(this.groups);
3876     },
3877
3878     // private - override to prevent hiding
3879     b4EndDrag: function(e) {
3880     },
3881
3882     // private - override to prevent moving
3883     endDrag : function(e){
3884         this.onEndDrag(this.dragData, e);
3885     },
3886
3887     // private
3888     onEndDrag : function(data, e){
3889     },
3890     
3891     // private - pin to cursor
3892     autoOffset : function(x, y) {
3893         this.setDelta(-12, -20);
3894     }    
3895 });/*
3896  * Based on:
3897  * Ext JS Library 1.1.1
3898  * Copyright(c) 2006-2007, Ext JS, LLC.
3899  *
3900  * Originally Released Under LGPL - original licence link has changed is not relivant.
3901  *
3902  * Fork - LGPL
3903  * <script type="text/javascript">
3904  */
3905
3906
3907 /**
3908  * @class Roo.dd.DropTarget
3909  * @extends Roo.dd.DDTarget
3910  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3912  * @constructor
3913  * @param {String/HTMLElement/Element} el The container element
3914  * @param {Object} config
3915  */
3916 Roo.dd.DropTarget = function(el, config){
3917     this.el = Roo.get(el);
3918     
3919     var listeners = false; ;
3920     if (config && config.listeners) {
3921         listeners= config.listeners;
3922         delete config.listeners;
3923     }
3924     Roo.apply(this, config);
3925     
3926     if(this.containerScroll){
3927         Roo.dd.ScrollManager.register(this.el);
3928     }
3929     this.addEvents( {
3930          /**
3931          * @scope Roo.dd.DropTarget
3932          */
3933          
3934          /**
3935          * @event enter
3936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3939          * 
3940          * IMPORTANT : it should set this.overClass and this.dropAllowed
3941          * 
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946         "enter" : true,
3947         
3948          /**
3949          * @event over
3950          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951          * This method will be called on every mouse movement while the drag source is over the drop target.
3952          * This default implementation simply returns the dropAllowed config value.
3953          * 
3954          * IMPORTANT : it should set this.dropAllowed
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959          
3960          */
3961         "over" : true,
3962         /**
3963          * @event out
3964          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3966          * overClass (if any) from the drop element.
3967          * 
3968          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969          * @param {Event} e The event
3970          * @param {Object} data An object containing arbitrary data supplied by the drag source
3971          */
3972          "out" : true,
3973          
3974         /**
3975          * @event drop
3976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3978          * implementation that does something to process the drop event and returns true so that the drag source's
3979          * repair action does not run.
3980          * 
3981          * IMPORTANT : it should set this.success
3982          * 
3983          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984          * @param {Event} e The event
3985          * @param {Object} data An object containing arbitrary data supplied by the drag source
3986         */
3987          "drop" : true
3988     });
3989             
3990      
3991     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3992         this.el.dom, 
3993         this.ddGroup || this.group,
3994         {
3995             isTarget: true,
3996             listeners : listeners || {} 
3997            
3998         
3999         }
4000     );
4001
4002 };
4003
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4005     /**
4006      * @cfg {String} overClass
4007      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4008      */
4009      /**
4010      * @cfg {String} ddGroup
4011      * The drag drop group to handle drop events for
4012      */
4013      
4014     /**
4015      * @cfg {String} dropAllowed
4016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4017      */
4018     dropAllowed : "x-dd-drop-ok",
4019     /**
4020      * @cfg {String} dropNotAllowed
4021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4022      */
4023     dropNotAllowed : "x-dd-drop-nodrop",
4024     /**
4025      * @cfg {boolean} success
4026      * set this after drop listener.. 
4027      */
4028     success : false,
4029     /**
4030      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031      * if the drop point is valid for over/enter..
4032      */
4033     valid : false,
4034     // private
4035     isTarget : true,
4036
4037     // private
4038     isNotifyTarget : true,
4039     
4040     /**
4041      * @hide
4042      */
4043     notifyEnter : function(dd, e, data)
4044     {
4045         this.valid = true;
4046         this.fireEvent('enter', dd, e, data);
4047         if(this.overClass){
4048             this.el.addClass(this.overClass);
4049         }
4050         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051             this.valid ? this.dropAllowed : this.dropNotAllowed
4052         );
4053     },
4054
4055     /**
4056      * @hide
4057      */
4058     notifyOver : function(dd, e, data)
4059     {
4060         this.valid = true;
4061         this.fireEvent('over', dd, e, data);
4062         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063             this.valid ? this.dropAllowed : this.dropNotAllowed
4064         );
4065     },
4066
4067     /**
4068      * @hide
4069      */
4070     notifyOut : function(dd, e, data)
4071     {
4072         this.fireEvent('out', dd, e, data);
4073         if(this.overClass){
4074             this.el.removeClass(this.overClass);
4075         }
4076     },
4077
4078     /**
4079      * @hide
4080      */
4081     notifyDrop : function(dd, e, data)
4082     {
4083         this.success = false;
4084         this.fireEvent('drop', dd, e, data);
4085         return this.success;
4086     }
4087 });/*
4088  * Based on:
4089  * Ext JS Library 1.1.1
4090  * Copyright(c) 2006-2007, Ext JS, LLC.
4091  *
4092  * Originally Released Under LGPL - original licence link has changed is not relivant.
4093  *
4094  * Fork - LGPL
4095  * <script type="text/javascript">
4096  */
4097
4098
4099 /**
4100  * @class Roo.dd.DragZone
4101  * @extends Roo.dd.DragSource
4102  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4104  * @constructor
4105  * @param {String/HTMLElement/Element} el The container element
4106  * @param {Object} config
4107  */
4108 Roo.dd.DragZone = function(el, config){
4109     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110     if(this.containerScroll){
4111         Roo.dd.ScrollManager.register(this.el);
4112     }
4113 };
4114
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4116     /**
4117      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118      * for auto scrolling during drag operations.
4119      */
4120     /**
4121      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122      * method after a failed drop (defaults to "c3daf9" - light blue)
4123      */
4124
4125     /**
4126      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127      * for a valid target to drag based on the mouse down. Override this method
4128      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130      * @param {EventObject} e The mouse down event
4131      * @return {Object} The dragData
4132      */
4133     getDragData : function(e){
4134         return Roo.dd.Registry.getHandleFromEvent(e);
4135     },
4136     
4137     /**
4138      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139      * this.dragData.ddel
4140      * @param {Number} x The x position of the click on the dragged object
4141      * @param {Number} y The y position of the click on the dragged object
4142      * @return {Boolean} true to continue the drag, false to cancel
4143      */
4144     onInitDrag : function(x, y){
4145         this.proxy.update(this.dragData.ddel.cloneNode(true));
4146         this.onStartDrag(x, y);
4147         return true;
4148     },
4149     
4150     /**
4151      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4152      */
4153     afterRepair : function(){
4154         if(Roo.enableFx){
4155             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4156         }
4157         this.dragging = false;
4158     },
4159
4160     /**
4161      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162      * the XY of this.dragData.ddel
4163      * @param {EventObject} e The mouse up event
4164      * @return {Array} The xy location (e.g. [100, 200])
4165      */
4166     getRepairXY : function(e){
4167         return Roo.Element.fly(this.dragData.ddel).getXY();  
4168     }
4169 });/*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179 /**
4180  * @class Roo.dd.DropZone
4181  * @extends Roo.dd.DropTarget
4182  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4184  * @constructor
4185  * @param {String/HTMLElement/Element} el The container element
4186  * @param {Object} config
4187  */
4188 Roo.dd.DropZone = function(el, config){
4189     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4190 };
4191
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4193     /**
4194      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4195      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196      * provide your own custom lookup.
4197      * @param {Event} e The event
4198      * @return {Object} data The custom data
4199      */
4200     getTargetFromEvent : function(e){
4201         return Roo.dd.Registry.getTargetFromEvent(e);
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206      * that it has registered.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeEnter : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4221      * overridden to provide the proper feedback.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228      * underlying {@link Roo.dd.StatusProxy} can be updated
4229      */
4230     onNodeOver : function(n, dd, e, data){
4231         return this.dropAllowed;
4232     },
4233
4234     /**
4235      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4237      * node-specific processing if necessary.
4238      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239      * {@link #getTargetFromEvent} for this node)
4240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241      * @param {Event} e The event
4242      * @param {Object} data An object containing arbitrary data supplied by the drag source
4243      */
4244     onNodeOut : function(n, dd, e, data){
4245         
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4251      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253      * {@link #getTargetFromEvent} for this node)
4254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255      * @param {Event} e The event
4256      * @param {Object} data An object containing arbitrary data supplied by the drag source
4257      * @return {Boolean} True if the drop was valid, else false
4258      */
4259     onNodeDrop : function(n, dd, e, data){
4260         return false;
4261     },
4262
4263     /**
4264      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4266      * it should be overridden to provide the proper feedback if necessary.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     onContainerOver : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4280      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283      * @param {Event} e The event
4284      * @param {Object} data An object containing arbitrary data supplied by the drag source
4285      * @return {Boolean} True if the drop was valid, else false
4286      */
4287     onContainerDrop : function(dd, e, data){
4288         return false;
4289     },
4290
4291     /**
4292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4294      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295      * you should override this method and provide a custom implementation.
4296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297      * @param {Event} e The event
4298      * @param {Object} data An object containing arbitrary data supplied by the drag source
4299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300      * underlying {@link Roo.dd.StatusProxy} can be updated
4301      */
4302     notifyEnter : function(dd, e, data){
4303         return this.dropNotAllowed;
4304     },
4305
4306     /**
4307      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308      * This method will be called on every mouse movement while the drag source is over the drop zone.
4309      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312      * registered node, it will call {@link #onContainerOver}.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag source
4316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317      * underlying {@link Roo.dd.StatusProxy} can be updated
4318      */
4319     notifyOver : function(dd, e, data){
4320         var n = this.getTargetFromEvent(e);
4321         if(!n){ // not over valid drop target
4322             if(this.lastOverNode){
4323                 this.onNodeOut(this.lastOverNode, dd, e, data);
4324                 this.lastOverNode = null;
4325             }
4326             return this.onContainerOver(dd, e, data);
4327         }
4328         if(this.lastOverNode != n){
4329             if(this.lastOverNode){
4330                 this.onNodeOut(this.lastOverNode, dd, e, data);
4331             }
4332             this.onNodeEnter(n, dd, e, data);
4333             this.lastOverNode = n;
4334         }
4335         return this.onNodeOver(n, dd, e, data);
4336     },
4337
4338     /**
4339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4341      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343      * @param {Event} e The event
4344      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4345      */
4346     notifyOut : function(dd, e, data){
4347         if(this.lastOverNode){
4348             this.onNodeOut(this.lastOverNode, dd, e, data);
4349             this.lastOverNode = null;
4350         }
4351     },
4352
4353     /**
4354      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4356      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357      * otherwise it will call {@link #onContainerDrop}.
4358      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359      * @param {Event} e The event
4360      * @param {Object} data An object containing arbitrary data supplied by the drag source
4361      * @return {Boolean} True if the drop was valid, else false
4362      */
4363     notifyDrop : function(dd, e, data){
4364         if(this.lastOverNode){
4365             this.onNodeOut(this.lastOverNode, dd, e, data);
4366             this.lastOverNode = null;
4367         }
4368         var n = this.getTargetFromEvent(e);
4369         return n ?
4370             this.onNodeDrop(n, dd, e, data) :
4371             this.onContainerDrop(dd, e, data);
4372     },
4373
4374     // private
4375     triggerCacheRefresh : function(){
4376         Roo.dd.DDM.refreshCache(this.groups);
4377     }  
4378 });/*
4379  * Based on:
4380  * Ext JS Library 1.1.1
4381  * Copyright(c) 2006-2007, Ext JS, LLC.
4382  *
4383  * Originally Released Under LGPL - original licence link has changed is not relivant.
4384  *
4385  * Fork - LGPL
4386  * <script type="text/javascript">
4387  */
4388
4389
4390 /**
4391  * @class Roo.data.SortTypes
4392  * @singleton
4393  * Defines the default sorting (casting?) comparison functions used when sorting data.
4394  */
4395 Roo.data.SortTypes = {
4396     /**
4397      * Default sort that does nothing
4398      * @param {Mixed} s The value being converted
4399      * @return {Mixed} The comparison value
4400      */
4401     none : function(s){
4402         return s;
4403     },
4404     
4405     /**
4406      * The regular expression used to strip tags
4407      * @type {RegExp}
4408      * @property
4409      */
4410     stripTagsRE : /<\/?[^>]+>/gi,
4411     
4412     /**
4413      * Strips all HTML tags to sort on text only
4414      * @param {Mixed} s The value being converted
4415      * @return {String} The comparison value
4416      */
4417     asText : function(s){
4418         return String(s).replace(this.stripTagsRE, "");
4419     },
4420     
4421     /**
4422      * Strips all HTML tags to sort on text only - Case insensitive
4423      * @param {Mixed} s The value being converted
4424      * @return {String} The comparison value
4425      */
4426     asUCText : function(s){
4427         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4428     },
4429     
4430     /**
4431      * Case insensitive string
4432      * @param {Mixed} s The value being converted
4433      * @return {String} The comparison value
4434      */
4435     asUCString : function(s) {
4436         return String(s).toUpperCase();
4437     },
4438     
4439     /**
4440      * Date sorting
4441      * @param {Mixed} s The value being converted
4442      * @return {Number} The comparison value
4443      */
4444     asDate : function(s) {
4445         if(!s){
4446             return 0;
4447         }
4448         if(s instanceof Date){
4449             return s.getTime();
4450         }
4451         return Date.parse(String(s));
4452     },
4453     
4454     /**
4455      * Float sorting
4456      * @param {Mixed} s The value being converted
4457      * @return {Float} The comparison value
4458      */
4459     asFloat : function(s) {
4460         var val = parseFloat(String(s).replace(/,/g, ""));
4461         if(isNaN(val)) val = 0;
4462         return val;
4463     },
4464     
4465     /**
4466      * Integer sorting
4467      * @param {Mixed} s The value being converted
4468      * @return {Number} The comparison value
4469      */
4470     asInt : function(s) {
4471         var val = parseInt(String(s).replace(/,/g, ""));
4472         if(isNaN(val)) val = 0;
4473         return val;
4474     }
4475 };/*
4476  * Based on:
4477  * Ext JS Library 1.1.1
4478  * Copyright(c) 2006-2007, Ext JS, LLC.
4479  *
4480  * Originally Released Under LGPL - original licence link has changed is not relivant.
4481  *
4482  * Fork - LGPL
4483  * <script type="text/javascript">
4484  */
4485
4486 /**
4487 * @class Roo.data.Record
4488  * Instances of this class encapsulate both record <em>definition</em> information, and record
4489  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4490  * to access Records cached in an {@link Roo.data.Store} object.<br>
4491  * <p>
4492  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4493  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4494  * objects.<br>
4495  * <p>
4496  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4497  * @constructor
4498  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4499  * {@link #create}. The parameters are the same.
4500  * @param {Array} data An associative Array of data values keyed by the field name.
4501  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4502  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4503  * not specified an integer id is generated.
4504  */
4505 Roo.data.Record = function(data, id){
4506     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4507     this.data = data;
4508 };
4509
4510 /**
4511  * Generate a constructor for a specific record layout.
4512  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4513  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4514  * Each field definition object may contain the following properties: <ul>
4515  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4516  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4517  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4518  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4519  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4520  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4521  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4522  * this may be omitted.</p></li>
4523  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4524  * <ul><li>auto (Default, implies no conversion)</li>
4525  * <li>string</li>
4526  * <li>int</li>
4527  * <li>float</li>
4528  * <li>boolean</li>
4529  * <li>date</li></ul></p></li>
4530  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4531  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4532  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4533  * by the Reader into an object that will be stored in the Record. It is passed the
4534  * following parameters:<ul>
4535  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4536  * </ul></p></li>
4537  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4538  * </ul>
4539  * <br>usage:<br><pre><code>
4540 var TopicRecord = Roo.data.Record.create(
4541     {name: 'title', mapping: 'topic_title'},
4542     {name: 'author', mapping: 'username'},
4543     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4544     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4545     {name: 'lastPoster', mapping: 'user2'},
4546     {name: 'excerpt', mapping: 'post_text'}
4547 );
4548
4549 var myNewRecord = new TopicRecord({
4550     title: 'Do my job please',
4551     author: 'noobie',
4552     totalPosts: 1,
4553     lastPost: new Date(),
4554     lastPoster: 'Animal',
4555     excerpt: 'No way dude!'
4556 });
4557 myStore.add(myNewRecord);
4558 </code></pre>
4559  * @method create
4560  * @static
4561  */
4562 Roo.data.Record.create = function(o){
4563     var f = function(){
4564         f.superclass.constructor.apply(this, arguments);
4565     };
4566     Roo.extend(f, Roo.data.Record);
4567     var p = f.prototype;
4568     p.fields = new Roo.util.MixedCollection(false, function(field){
4569         return field.name;
4570     });
4571     for(var i = 0, len = o.length; i < len; i++){
4572         p.fields.add(new Roo.data.Field(o[i]));
4573     }
4574     f.getField = function(name){
4575         return p.fields.get(name);  
4576     };
4577     return f;
4578 };
4579
4580 Roo.data.Record.AUTO_ID = 1000;
4581 Roo.data.Record.EDIT = 'edit';
4582 Roo.data.Record.REJECT = 'reject';
4583 Roo.data.Record.COMMIT = 'commit';
4584
4585 Roo.data.Record.prototype = {
4586     /**
4587      * Readonly flag - true if this record has been modified.
4588      * @type Boolean
4589      */
4590     dirty : false,
4591     editing : false,
4592     error: null,
4593     modified: null,
4594
4595     // private
4596     join : function(store){
4597         this.store = store;
4598     },
4599
4600     /**
4601      * Set the named field to the specified value.
4602      * @param {String} name The name of the field to set.
4603      * @param {Object} value The value to set the field to.
4604      */
4605     set : function(name, value){
4606         if(this.data[name] == value){
4607             return;
4608         }
4609         this.dirty = true;
4610         if(!this.modified){
4611             this.modified = {};
4612         }
4613         if(typeof this.modified[name] == 'undefined'){
4614             this.modified[name] = this.data[name];
4615         }
4616         this.data[name] = value;
4617         if(!this.editing && this.store){
4618             this.store.afterEdit(this);
4619         }       
4620     },
4621
4622     /**
4623      * Get the value of the named field.
4624      * @param {String} name The name of the field to get the value of.
4625      * @return {Object} The value of the field.
4626      */
4627     get : function(name){
4628         return this.data[name]; 
4629     },
4630
4631     // private
4632     beginEdit : function(){
4633         this.editing = true;
4634         this.modified = {}; 
4635     },
4636
4637     // private
4638     cancelEdit : function(){
4639         this.editing = false;
4640         delete this.modified;
4641     },
4642
4643     // private
4644     endEdit : function(){
4645         this.editing = false;
4646         if(this.dirty && this.store){
4647             this.store.afterEdit(this);
4648         }
4649     },
4650
4651     /**
4652      * Usually called by the {@link Roo.data.Store} which owns the Record.
4653      * Rejects all changes made to the Record since either creation, or the last commit operation.
4654      * Modified fields are reverted to their original values.
4655      * <p>
4656      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4657      * of reject operations.
4658      */
4659     reject : function(){
4660         var m = this.modified;
4661         for(var n in m){
4662             if(typeof m[n] != "function"){
4663                 this.data[n] = m[n];
4664             }
4665         }
4666         this.dirty = false;
4667         delete this.modified;
4668         this.editing = false;
4669         if(this.store){
4670             this.store.afterReject(this);
4671         }
4672     },
4673
4674     /**
4675      * Usually called by the {@link Roo.data.Store} which owns the Record.
4676      * Commits all changes made to the Record since either creation, or the last commit operation.
4677      * <p>
4678      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4679      * of commit operations.
4680      */
4681     commit : function(){
4682         this.dirty = false;
4683         delete this.modified;
4684         this.editing = false;
4685         if(this.store){
4686             this.store.afterCommit(this);
4687         }
4688     },
4689
4690     // private
4691     hasError : function(){
4692         return this.error != null;
4693     },
4694
4695     // private
4696     clearError : function(){
4697         this.error = null;
4698     },
4699
4700     /**
4701      * Creates a copy of this record.
4702      * @param {String} id (optional) A new record id if you don't want to use this record's id
4703      * @return {Record}
4704      */
4705     copy : function(newId) {
4706         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4707     }
4708 };/*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718
4719
4720
4721 /**
4722  * @class Roo.data.Store
4723  * @extends Roo.util.Observable
4724  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4725  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4726  * <p>
4727  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4728  * has no knowledge of the format of the data returned by the Proxy.<br>
4729  * <p>
4730  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4731  * instances from the data object. These records are cached and made available through accessor functions.
4732  * @constructor
4733  * Creates a new Store.
4734  * @param {Object} config A config object containing the objects needed for the Store to access data,
4735  * and read the data into Records.
4736  */
4737 Roo.data.Store = function(config){
4738     this.data = new Roo.util.MixedCollection(false);
4739     this.data.getKey = function(o){
4740         return o.id;
4741     };
4742     this.baseParams = {};
4743     // private
4744     this.paramNames = {
4745         "start" : "start",
4746         "limit" : "limit",
4747         "sort" : "sort",
4748         "dir" : "dir",
4749         "multisort" : "_multisort"
4750     };
4751
4752     if(config && config.data){
4753         this.inlineData = config.data;
4754         delete config.data;
4755     }
4756
4757     Roo.apply(this, config);
4758     
4759     if(this.reader){ // reader passed
4760         this.reader = Roo.factory(this.reader, Roo.data);
4761         this.reader.xmodule = this.xmodule || false;
4762         if(!this.recordType){
4763             this.recordType = this.reader.recordType;
4764         }
4765         if(this.reader.onMetaChange){
4766             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4767         }
4768     }
4769
4770     if(this.recordType){
4771         this.fields = this.recordType.prototype.fields;
4772     }
4773     this.modified = [];
4774
4775     this.addEvents({
4776         /**
4777          * @event datachanged
4778          * Fires when the data cache has changed, and a widget which is using this Store
4779          * as a Record cache should refresh its view.
4780          * @param {Store} this
4781          */
4782         datachanged : true,
4783         /**
4784          * @event metachange
4785          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4786          * @param {Store} this
4787          * @param {Object} meta The JSON metadata
4788          */
4789         metachange : true,
4790         /**
4791          * @event add
4792          * Fires when Records have been added to the Store
4793          * @param {Store} this
4794          * @param {Roo.data.Record[]} records The array of Records added
4795          * @param {Number} index The index at which the record(s) were added
4796          */
4797         add : true,
4798         /**
4799          * @event remove
4800          * Fires when a Record has been removed from the Store
4801          * @param {Store} this
4802          * @param {Roo.data.Record} record The Record that was removed
4803          * @param {Number} index The index at which the record was removed
4804          */
4805         remove : true,
4806         /**
4807          * @event update
4808          * Fires when a Record has been updated
4809          * @param {Store} this
4810          * @param {Roo.data.Record} record The Record that was updated
4811          * @param {String} operation The update operation being performed.  Value may be one of:
4812          * <pre><code>
4813  Roo.data.Record.EDIT
4814  Roo.data.Record.REJECT
4815  Roo.data.Record.COMMIT
4816          * </code></pre>
4817          */
4818         update : true,
4819         /**
4820          * @event clear
4821          * Fires when the data cache has been cleared.
4822          * @param {Store} this
4823          */
4824         clear : true,
4825         /**
4826          * @event beforeload
4827          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4828          * the load action will be canceled.
4829          * @param {Store} this
4830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4831          */
4832         beforeload : true,
4833         /**
4834          * @event beforeloadadd
4835          * Fires after a new set of Records has been loaded.
4836          * @param {Store} this
4837          * @param {Roo.data.Record[]} records The Records that were loaded
4838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4839          */
4840         beforeloadadd : true,
4841         /**
4842          * @event load
4843          * Fires after a new set of Records has been loaded, before they are added to the store.
4844          * @param {Store} this
4845          * @param {Roo.data.Record[]} records The Records that were loaded
4846          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4847          * @params {Object} return from reader
4848          */
4849         load : true,
4850         /**
4851          * @event loadexception
4852          * Fires if an exception occurs in the Proxy during loading.
4853          * Called with the signature of the Proxy's "loadexception" event.
4854          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4855          * 
4856          * @param {Proxy} 
4857          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4858          * @param {Object} load options 
4859          * @param {Object} jsonData from your request (normally this contains the Exception)
4860          */
4861         loadexception : true
4862     });
4863     
4864     if(this.proxy){
4865         this.proxy = Roo.factory(this.proxy, Roo.data);
4866         this.proxy.xmodule = this.xmodule || false;
4867         this.relayEvents(this.proxy,  ["loadexception"]);
4868     }
4869     this.sortToggle = {};
4870     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4871
4872     Roo.data.Store.superclass.constructor.call(this);
4873
4874     if(this.inlineData){
4875         this.loadData(this.inlineData);
4876         delete this.inlineData;
4877     }
4878 };
4879
4880 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4881      /**
4882     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4883     * without a remote query - used by combo/forms at present.
4884     */
4885     
4886     /**
4887     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4888     */
4889     /**
4890     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4891     */
4892     /**
4893     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4894     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4895     */
4896     /**
4897     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4898     * on any HTTP request
4899     */
4900     /**
4901     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4902     */
4903     /**
4904     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4905     */
4906     multiSort: false,
4907     /**
4908     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4909     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4910     */
4911     remoteSort : false,
4912
4913     /**
4914     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4915      * loaded or when a record is removed. (defaults to false).
4916     */
4917     pruneModifiedRecords : false,
4918
4919     // private
4920     lastOptions : null,
4921
4922     /**
4923      * Add Records to the Store and fires the add event.
4924      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4925      */
4926     add : function(records){
4927         records = [].concat(records);
4928         for(var i = 0, len = records.length; i < len; i++){
4929             records[i].join(this);
4930         }
4931         var index = this.data.length;
4932         this.data.addAll(records);
4933         this.fireEvent("add", this, records, index);
4934     },
4935
4936     /**
4937      * Remove a Record from the Store and fires the remove event.
4938      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4939      */
4940     remove : function(record){
4941         var index = this.data.indexOf(record);
4942         this.data.removeAt(index);
4943         if(this.pruneModifiedRecords){
4944             this.modified.remove(record);
4945         }
4946         this.fireEvent("remove", this, record, index);
4947     },
4948
4949     /**
4950      * Remove all Records from the Store and fires the clear event.
4951      */
4952     removeAll : function(){
4953         this.data.clear();
4954         if(this.pruneModifiedRecords){
4955             this.modified = [];
4956         }
4957         this.fireEvent("clear", this);
4958     },
4959
4960     /**
4961      * Inserts Records to the Store at the given index and fires the add event.
4962      * @param {Number} index The start index at which to insert the passed Records.
4963      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4964      */
4965     insert : function(index, records){
4966         records = [].concat(records);
4967         for(var i = 0, len = records.length; i < len; i++){
4968             this.data.insert(index, records[i]);
4969             records[i].join(this);
4970         }
4971         this.fireEvent("add", this, records, index);
4972     },
4973
4974     /**
4975      * Get the index within the cache of the passed Record.
4976      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4977      * @return {Number} The index of the passed Record. Returns -1 if not found.
4978      */
4979     indexOf : function(record){
4980         return this.data.indexOf(record);
4981     },
4982
4983     /**
4984      * Get the index within the cache of the Record with the passed id.
4985      * @param {String} id The id of the Record to find.
4986      * @return {Number} The index of the Record. Returns -1 if not found.
4987      */
4988     indexOfId : function(id){
4989         return this.data.indexOfKey(id);
4990     },
4991
4992     /**
4993      * Get the Record with the specified id.
4994      * @param {String} id The id of the Record to find.
4995      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4996      */
4997     getById : function(id){
4998         return this.data.key(id);
4999     },
5000
5001     /**
5002      * Get the Record at the specified index.
5003      * @param {Number} index The index of the Record to find.
5004      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5005      */
5006     getAt : function(index){
5007         return this.data.itemAt(index);
5008     },
5009
5010     /**
5011      * Returns a range of Records between specified indices.
5012      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5013      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5014      * @return {Roo.data.Record[]} An array of Records
5015      */
5016     getRange : function(start, end){
5017         return this.data.getRange(start, end);
5018     },
5019
5020     // private
5021     storeOptions : function(o){
5022         o = Roo.apply({}, o);
5023         delete o.callback;
5024         delete o.scope;
5025         this.lastOptions = o;
5026     },
5027
5028     /**
5029      * Loads the Record cache from the configured Proxy using the configured Reader.
5030      * <p>
5031      * If using remote paging, then the first load call must specify the <em>start</em>
5032      * and <em>limit</em> properties in the options.params property to establish the initial
5033      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5034      * <p>
5035      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5036      * and this call will return before the new data has been loaded. Perform any post-processing
5037      * in a callback function, or in a "load" event handler.</strong>
5038      * <p>
5039      * @param {Object} options An object containing properties which control loading options:<ul>
5040      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5041      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5042      * passed the following arguments:<ul>
5043      * <li>r : Roo.data.Record[]</li>
5044      * <li>options: Options object from the load call</li>
5045      * <li>success: Boolean success indicator</li></ul></li>
5046      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5047      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5048      * </ul>
5049      */
5050     load : function(options){
5051         options = options || {};
5052         if(this.fireEvent("beforeload", this, options) !== false){
5053             this.storeOptions(options);
5054             var p = Roo.apply(options.params || {}, this.baseParams);
5055             // if meta was not loaded from remote source.. try requesting it.
5056             if (!this.reader.metaFromRemote) {
5057                 p._requestMeta = 1;
5058             }
5059             if(this.sortInfo && this.remoteSort){
5060                 var pn = this.paramNames;
5061                 p[pn["sort"]] = this.sortInfo.field;
5062                 p[pn["dir"]] = this.sortInfo.direction;
5063             }
5064             if (this.multiSort) {
5065                 var pn = this.paramNames;
5066                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5067             }
5068             
5069             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5070         }
5071     },
5072
5073     /**
5074      * Reloads the Record cache from the configured Proxy using the configured Reader and
5075      * the options from the last load operation performed.
5076      * @param {Object} options (optional) An object containing properties which may override the options
5077      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5078      * the most recently used options are reused).
5079      */
5080     reload : function(options){
5081         this.load(Roo.applyIf(options||{}, this.lastOptions));
5082     },
5083
5084     // private
5085     // Called as a callback by the Reader during a load operation.
5086     loadRecords : function(o, options, success){
5087         if(!o || success === false){
5088             if(success !== false){
5089                 this.fireEvent("load", this, [], options, o);
5090             }
5091             if(options.callback){
5092                 options.callback.call(options.scope || this, [], options, false);
5093             }
5094             return;
5095         }
5096         // if data returned failure - throw an exception.
5097         if (o.success === false) {
5098             // show a message if no listener is registered.
5099             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5100                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5101             }
5102             // loadmask wil be hooked into this..
5103             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5104             return;
5105         }
5106         var r = o.records, t = o.totalRecords || r.length;
5107         
5108         this.fireEvent("beforeloadadd", this, r, options, o);
5109         
5110         if(!options || options.add !== true){
5111             if(this.pruneModifiedRecords){
5112                 this.modified = [];
5113             }
5114             for(var i = 0, len = r.length; i < len; i++){
5115                 r[i].join(this);
5116             }
5117             if(this.snapshot){
5118                 this.data = this.snapshot;
5119                 delete this.snapshot;
5120             }
5121             this.data.clear();
5122             this.data.addAll(r);
5123             this.totalLength = t;
5124             this.applySort();
5125             this.fireEvent("datachanged", this);
5126         }else{
5127             this.totalLength = Math.max(t, this.data.length+r.length);
5128             this.add(r);
5129         }
5130         this.fireEvent("load", this, r, options, o);
5131         if(options.callback){
5132             options.callback.call(options.scope || this, r, options, true);
5133         }
5134     },
5135
5136
5137     /**
5138      * Loads data from a passed data block. A Reader which understands the format of the data
5139      * must have been configured in the constructor.
5140      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5141      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5142      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5143      */
5144     loadData : function(o, append){
5145         var r = this.reader.readRecords(o);
5146         this.loadRecords(r, {add: append}, true);
5147     },
5148
5149     /**
5150      * Gets the number of cached records.
5151      * <p>
5152      * <em>If using paging, this may not be the total size of the dataset. If the data object
5153      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5154      * the data set size</em>
5155      */
5156     getCount : function(){
5157         return this.data.length || 0;
5158     },
5159
5160     /**
5161      * Gets the total number of records in the dataset as returned by the server.
5162      * <p>
5163      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5164      * the dataset size</em>
5165      */
5166     getTotalCount : function(){
5167         return this.totalLength || 0;
5168     },
5169
5170     /**
5171      * Returns the sort state of the Store as an object with two properties:
5172      * <pre><code>
5173  field {String} The name of the field by which the Records are sorted
5174  direction {String} The sort order, "ASC" or "DESC"
5175      * </code></pre>
5176      */
5177     getSortState : function(){
5178         return this.sortInfo;
5179     },
5180
5181     // private
5182     applySort : function(){
5183         if(this.sortInfo && !this.remoteSort){
5184             var s = this.sortInfo, f = s.field;
5185             var st = this.fields.get(f).sortType;
5186             var fn = function(r1, r2){
5187                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5188                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5189             };
5190             this.data.sort(s.direction, fn);
5191             if(this.snapshot && this.snapshot != this.data){
5192                 this.snapshot.sort(s.direction, fn);
5193             }
5194         }
5195     },
5196
5197     /**
5198      * Sets the default sort column and order to be used by the next load operation.
5199      * @param {String} fieldName The name of the field to sort by.
5200      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5201      */
5202     setDefaultSort : function(field, dir){
5203         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5204     },
5205
5206     /**
5207      * Sort the Records.
5208      * If remote sorting is used, the sort is performed on the server, and the cache is
5209      * reloaded. If local sorting is used, the cache is sorted internally.
5210      * @param {String} fieldName The name of the field to sort by.
5211      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5212      */
5213     sort : function(fieldName, dir){
5214         var f = this.fields.get(fieldName);
5215         if(!dir){
5216             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5217             
5218             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5219                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5220             }else{
5221                 dir = f.sortDir;
5222             }
5223         }
5224         this.sortToggle[f.name] = dir;
5225         this.sortInfo = {field: f.name, direction: dir};
5226         if(!this.remoteSort){
5227             this.applySort();
5228             this.fireEvent("datachanged", this);
5229         }else{
5230             this.load(this.lastOptions);
5231         }
5232     },
5233
5234     /**
5235      * Calls the specified function for each of the Records in the cache.
5236      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5237      * Returning <em>false</em> aborts and exits the iteration.
5238      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5239      */
5240     each : function(fn, scope){
5241         this.data.each(fn, scope);
5242     },
5243
5244     /**
5245      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5246      * (e.g., during paging).
5247      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5248      */
5249     getModifiedRecords : function(){
5250         return this.modified;
5251     },
5252
5253     // private
5254     createFilterFn : function(property, value, anyMatch){
5255         if(!value.exec){ // not a regex
5256             value = String(value);
5257             if(value.length == 0){
5258                 return false;
5259             }
5260             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5261         }
5262         return function(r){
5263             return value.test(r.data[property]);
5264         };
5265     },
5266
5267     /**
5268      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5269      * @param {String} property A field on your records
5270      * @param {Number} start The record index to start at (defaults to 0)
5271      * @param {Number} end The last record index to include (defaults to length - 1)
5272      * @return {Number} The sum
5273      */
5274     sum : function(property, start, end){
5275         var rs = this.data.items, v = 0;
5276         start = start || 0;
5277         end = (end || end === 0) ? end : rs.length-1;
5278
5279         for(var i = start; i <= end; i++){
5280             v += (rs[i].data[property] || 0);
5281         }
5282         return v;
5283     },
5284
5285     /**
5286      * Filter the records by a specified property.
5287      * @param {String} field A field on your records
5288      * @param {String/RegExp} value Either a string that the field
5289      * should start with or a RegExp to test against the field
5290      * @param {Boolean} anyMatch True to match any part not just the beginning
5291      */
5292     filter : function(property, value, anyMatch){
5293         var fn = this.createFilterFn(property, value, anyMatch);
5294         return fn ? this.filterBy(fn) : this.clearFilter();
5295     },
5296
5297     /**
5298      * Filter by a function. The specified function will be called with each
5299      * record in this data source. If the function returns true the record is included,
5300      * otherwise it is filtered.
5301      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5302      * @param {Object} scope (optional) The scope of the function (defaults to this)
5303      */
5304     filterBy : function(fn, scope){
5305         this.snapshot = this.snapshot || this.data;
5306         this.data = this.queryBy(fn, scope||this);
5307         this.fireEvent("datachanged", this);
5308     },
5309
5310     /**
5311      * Query the records by a specified property.
5312      * @param {String} field A field on your records
5313      * @param {String/RegExp} value Either a string that the field
5314      * should start with or a RegExp to test against the field
5315      * @param {Boolean} anyMatch True to match any part not just the beginning
5316      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5317      */
5318     query : function(property, value, anyMatch){
5319         var fn = this.createFilterFn(property, value, anyMatch);
5320         return fn ? this.queryBy(fn) : this.data.clone();
5321     },
5322
5323     /**
5324      * Query by a function. The specified function will be called with each
5325      * record in this data source. If the function returns true the record is included
5326      * in the results.
5327      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5328      * @param {Object} scope (optional) The scope of the function (defaults to this)
5329       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5330      **/
5331     queryBy : function(fn, scope){
5332         var data = this.snapshot || this.data;
5333         return data.filterBy(fn, scope||this);
5334     },
5335
5336     /**
5337      * Collects unique values for a particular dataIndex from this store.
5338      * @param {String} dataIndex The property to collect
5339      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5340      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5341      * @return {Array} An array of the unique values
5342      **/
5343     collect : function(dataIndex, allowNull, bypassFilter){
5344         var d = (bypassFilter === true && this.snapshot) ?
5345                 this.snapshot.items : this.data.items;
5346         var v, sv, r = [], l = {};
5347         for(var i = 0, len = d.length; i < len; i++){
5348             v = d[i].data[dataIndex];
5349             sv = String(v);
5350             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5351                 l[sv] = true;
5352                 r[r.length] = v;
5353             }
5354         }
5355         return r;
5356     },
5357
5358     /**
5359      * Revert to a view of the Record cache with no filtering applied.
5360      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5361      */
5362     clearFilter : function(suppressEvent){
5363         if(this.snapshot && this.snapshot != this.data){
5364             this.data = this.snapshot;
5365             delete this.snapshot;
5366             if(suppressEvent !== true){
5367                 this.fireEvent("datachanged", this);
5368             }
5369         }
5370     },
5371
5372     // private
5373     afterEdit : function(record){
5374         if(this.modified.indexOf(record) == -1){
5375             this.modified.push(record);
5376         }
5377         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5378     },
5379     
5380     // private
5381     afterReject : function(record){
5382         this.modified.remove(record);
5383         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5384     },
5385
5386     // private
5387     afterCommit : function(record){
5388         this.modified.remove(record);
5389         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5390     },
5391
5392     /**
5393      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5394      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5395      */
5396     commitChanges : function(){
5397         var m = this.modified.slice(0);
5398         this.modified = [];
5399         for(var i = 0, len = m.length; i < len; i++){
5400             m[i].commit();
5401         }
5402     },
5403
5404     /**
5405      * Cancel outstanding changes on all changed records.
5406      */
5407     rejectChanges : function(){
5408         var m = this.modified.slice(0);
5409         this.modified = [];
5410         for(var i = 0, len = m.length; i < len; i++){
5411             m[i].reject();
5412         }
5413     },
5414
5415     onMetaChange : function(meta, rtype, o){
5416         this.recordType = rtype;
5417         this.fields = rtype.prototype.fields;
5418         delete this.snapshot;
5419         this.sortInfo = meta.sortInfo || this.sortInfo;
5420         this.modified = [];
5421         this.fireEvent('metachange', this, this.reader.meta);
5422     },
5423     
5424     moveIndex : function(data, type)
5425     {
5426         var index = this.indexOf(data);
5427         
5428         var newIndex = index + type;
5429         
5430         this.remove(data);
5431         
5432         this.insert(newIndex, data);
5433         
5434     }
5435 });/*
5436  * Based on:
5437  * Ext JS Library 1.1.1
5438  * Copyright(c) 2006-2007, Ext JS, LLC.
5439  *
5440  * Originally Released Under LGPL - original licence link has changed is not relivant.
5441  *
5442  * Fork - LGPL
5443  * <script type="text/javascript">
5444  */
5445
5446 /**
5447  * @class Roo.data.SimpleStore
5448  * @extends Roo.data.Store
5449  * Small helper class to make creating Stores from Array data easier.
5450  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5451  * @cfg {Array} fields An array of field definition objects, or field name strings.
5452  * @cfg {Array} data The multi-dimensional array of data
5453  * @constructor
5454  * @param {Object} config
5455  */
5456 Roo.data.SimpleStore = function(config){
5457     Roo.data.SimpleStore.superclass.constructor.call(this, {
5458         isLocal : true,
5459         reader: new Roo.data.ArrayReader({
5460                 id: config.id
5461             },
5462             Roo.data.Record.create(config.fields)
5463         ),
5464         proxy : new Roo.data.MemoryProxy(config.data)
5465     });
5466     this.load();
5467 };
5468 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5469  * Based on:
5470  * Ext JS Library 1.1.1
5471  * Copyright(c) 2006-2007, Ext JS, LLC.
5472  *
5473  * Originally Released Under LGPL - original licence link has changed is not relivant.
5474  *
5475  * Fork - LGPL
5476  * <script type="text/javascript">
5477  */
5478
5479 /**
5480 /**
5481  * @extends Roo.data.Store
5482  * @class Roo.data.JsonStore
5483  * Small helper class to make creating Stores for JSON data easier. <br/>
5484 <pre><code>
5485 var store = new Roo.data.JsonStore({
5486     url: 'get-images.php',
5487     root: 'images',
5488     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5489 });
5490 </code></pre>
5491  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5492  * JsonReader and HttpProxy (unless inline data is provided).</b>
5493  * @cfg {Array} fields An array of field definition objects, or field name strings.
5494  * @constructor
5495  * @param {Object} config
5496  */
5497 Roo.data.JsonStore = function(c){
5498     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5499         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5500         reader: new Roo.data.JsonReader(c, c.fields)
5501     }));
5502 };
5503 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5504  * Based on:
5505  * Ext JS Library 1.1.1
5506  * Copyright(c) 2006-2007, Ext JS, LLC.
5507  *
5508  * Originally Released Under LGPL - original licence link has changed is not relivant.
5509  *
5510  * Fork - LGPL
5511  * <script type="text/javascript">
5512  */
5513
5514  
5515 Roo.data.Field = function(config){
5516     if(typeof config == "string"){
5517         config = {name: config};
5518     }
5519     Roo.apply(this, config);
5520     
5521     if(!this.type){
5522         this.type = "auto";
5523     }
5524     
5525     var st = Roo.data.SortTypes;
5526     // named sortTypes are supported, here we look them up
5527     if(typeof this.sortType == "string"){
5528         this.sortType = st[this.sortType];
5529     }
5530     
5531     // set default sortType for strings and dates
5532     if(!this.sortType){
5533         switch(this.type){
5534             case "string":
5535                 this.sortType = st.asUCString;
5536                 break;
5537             case "date":
5538                 this.sortType = st.asDate;
5539                 break;
5540             default:
5541                 this.sortType = st.none;
5542         }
5543     }
5544
5545     // define once
5546     var stripRe = /[\$,%]/g;
5547
5548     // prebuilt conversion function for this field, instead of
5549     // switching every time we're reading a value
5550     if(!this.convert){
5551         var cv, dateFormat = this.dateFormat;
5552         switch(this.type){
5553             case "":
5554             case "auto":
5555             case undefined:
5556                 cv = function(v){ return v; };
5557                 break;
5558             case "string":
5559                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5560                 break;
5561             case "int":
5562                 cv = function(v){
5563                     return v !== undefined && v !== null && v !== '' ?
5564                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5565                     };
5566                 break;
5567             case "float":
5568                 cv = function(v){
5569                     return v !== undefined && v !== null && v !== '' ?
5570                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5571                     };
5572                 break;
5573             case "bool":
5574             case "boolean":
5575                 cv = function(v){ return v === true || v === "true" || v == 1; };
5576                 break;
5577             case "date":
5578                 cv = function(v){
5579                     if(!v){
5580                         return '';
5581                     }
5582                     if(v instanceof Date){
5583                         return v;
5584                     }
5585                     if(dateFormat){
5586                         if(dateFormat == "timestamp"){
5587                             return new Date(v*1000);
5588                         }
5589                         return Date.parseDate(v, dateFormat);
5590                     }
5591                     var parsed = Date.parse(v);
5592                     return parsed ? new Date(parsed) : null;
5593                 };
5594              break;
5595             
5596         }
5597         this.convert = cv;
5598     }
5599 };
5600
5601 Roo.data.Field.prototype = {
5602     dateFormat: null,
5603     defaultValue: "",
5604     mapping: null,
5605     sortType : null,
5606     sortDir : "ASC"
5607 };/*
5608  * Based on:
5609  * Ext JS Library 1.1.1
5610  * Copyright(c) 2006-2007, Ext JS, LLC.
5611  *
5612  * Originally Released Under LGPL - original licence link has changed is not relivant.
5613  *
5614  * Fork - LGPL
5615  * <script type="text/javascript">
5616  */
5617  
5618 // Base class for reading structured data from a data source.  This class is intended to be
5619 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5620
5621 /**
5622  * @class Roo.data.DataReader
5623  * Base class for reading structured data from a data source.  This class is intended to be
5624  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5625  */
5626
5627 Roo.data.DataReader = function(meta, recordType){
5628     
5629     this.meta = meta;
5630     
5631     this.recordType = recordType instanceof Array ? 
5632         Roo.data.Record.create(recordType) : recordType;
5633 };
5634
5635 Roo.data.DataReader.prototype = {
5636      /**
5637      * Create an empty record
5638      * @param {Object} data (optional) - overlay some values
5639      * @return {Roo.data.Record} record created.
5640      */
5641     newRow :  function(d) {
5642         var da =  {};
5643         this.recordType.prototype.fields.each(function(c) {
5644             switch( c.type) {
5645                 case 'int' : da[c.name] = 0; break;
5646                 case 'date' : da[c.name] = new Date(); break;
5647                 case 'float' : da[c.name] = 0.0; break;
5648                 case 'boolean' : da[c.name] = false; break;
5649                 default : da[c.name] = ""; break;
5650             }
5651             
5652         });
5653         return new this.recordType(Roo.apply(da, d));
5654     }
5655     
5656 };/*
5657  * Based on:
5658  * Ext JS Library 1.1.1
5659  * Copyright(c) 2006-2007, Ext JS, LLC.
5660  *
5661  * Originally Released Under LGPL - original licence link has changed is not relivant.
5662  *
5663  * Fork - LGPL
5664  * <script type="text/javascript">
5665  */
5666
5667 /**
5668  * @class Roo.data.DataProxy
5669  * @extends Roo.data.Observable
5670  * This class is an abstract base class for implementations which provide retrieval of
5671  * unformatted data objects.<br>
5672  * <p>
5673  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5674  * (of the appropriate type which knows how to parse the data object) to provide a block of
5675  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5676  * <p>
5677  * Custom implementations must implement the load method as described in
5678  * {@link Roo.data.HttpProxy#load}.
5679  */
5680 Roo.data.DataProxy = function(){
5681     this.addEvents({
5682         /**
5683          * @event beforeload
5684          * Fires before a network request is made to retrieve a data object.
5685          * @param {Object} This DataProxy object.
5686          * @param {Object} params The params parameter to the load function.
5687          */
5688         beforeload : true,
5689         /**
5690          * @event load
5691          * Fires before the load method's callback is called.
5692          * @param {Object} This DataProxy object.
5693          * @param {Object} o The data object.
5694          * @param {Object} arg The callback argument object passed to the load function.
5695          */
5696         load : true,
5697         /**
5698          * @event loadexception
5699          * Fires if an Exception occurs during data retrieval.
5700          * @param {Object} This DataProxy object.
5701          * @param {Object} o The data object.
5702          * @param {Object} arg The callback argument object passed to the load function.
5703          * @param {Object} e The Exception.
5704          */
5705         loadexception : true
5706     });
5707     Roo.data.DataProxy.superclass.constructor.call(this);
5708 };
5709
5710 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5711
5712     /**
5713      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5714      */
5715 /*
5716  * Based on:
5717  * Ext JS Library 1.1.1
5718  * Copyright(c) 2006-2007, Ext JS, LLC.
5719  *
5720  * Originally Released Under LGPL - original licence link has changed is not relivant.
5721  *
5722  * Fork - LGPL
5723  * <script type="text/javascript">
5724  */
5725 /**
5726  * @class Roo.data.MemoryProxy
5727  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5728  * to the Reader when its load method is called.
5729  * @constructor
5730  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5731  */
5732 Roo.data.MemoryProxy = function(data){
5733     if (data.data) {
5734         data = data.data;
5735     }
5736     Roo.data.MemoryProxy.superclass.constructor.call(this);
5737     this.data = data;
5738 };
5739
5740 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5741     /**
5742      * Load data from the requested source (in this case an in-memory
5743      * data object passed to the constructor), read the data object into
5744      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5745      * process that block using the passed callback.
5746      * @param {Object} params This parameter is not used by the MemoryProxy class.
5747      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5748      * object into a block of Roo.data.Records.
5749      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5750      * The function must be passed <ul>
5751      * <li>The Record block object</li>
5752      * <li>The "arg" argument from the load function</li>
5753      * <li>A boolean success indicator</li>
5754      * </ul>
5755      * @param {Object} scope The scope in which to call the callback
5756      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5757      */
5758     load : function(params, reader, callback, scope, arg){
5759         params = params || {};
5760         var result;
5761         try {
5762             result = reader.readRecords(this.data);
5763         }catch(e){
5764             this.fireEvent("loadexception", this, arg, null, e);
5765             callback.call(scope, null, arg, false);
5766             return;
5767         }
5768         callback.call(scope, result, arg, true);
5769     },
5770     
5771     // private
5772     update : function(params, records){
5773         
5774     }
5775 });/*
5776  * Based on:
5777  * Ext JS Library 1.1.1
5778  * Copyright(c) 2006-2007, Ext JS, LLC.
5779  *
5780  * Originally Released Under LGPL - original licence link has changed is not relivant.
5781  *
5782  * Fork - LGPL
5783  * <script type="text/javascript">
5784  */
5785 /**
5786  * @class Roo.data.HttpProxy
5787  * @extends Roo.data.DataProxy
5788  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5789  * configured to reference a certain URL.<br><br>
5790  * <p>
5791  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5792  * from which the running page was served.<br><br>
5793  * <p>
5794  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5795  * <p>
5796  * Be aware that to enable the browser to parse an XML document, the server must set
5797  * the Content-Type header in the HTTP response to "text/xml".
5798  * @constructor
5799  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5800  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5801  * will be used to make the request.
5802  */
5803 Roo.data.HttpProxy = function(conn){
5804     Roo.data.HttpProxy.superclass.constructor.call(this);
5805     // is conn a conn config or a real conn?
5806     this.conn = conn;
5807     this.useAjax = !conn || !conn.events;
5808   
5809 };
5810
5811 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5812     // thse are take from connection...
5813     
5814     /**
5815      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5816      */
5817     /**
5818      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5819      * extra parameters to each request made by this object. (defaults to undefined)
5820      */
5821     /**
5822      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5823      *  to each request made by this object. (defaults to undefined)
5824      */
5825     /**
5826      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5827      */
5828     /**
5829      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5830      */
5831      /**
5832      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5833      * @type Boolean
5834      */
5835   
5836
5837     /**
5838      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5839      * @type Boolean
5840      */
5841     /**
5842      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5843      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5844      * a finer-grained basis than the DataProxy events.
5845      */
5846     getConnection : function(){
5847         return this.useAjax ? Roo.Ajax : this.conn;
5848     },
5849
5850     /**
5851      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5852      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5853      * process that block using the passed callback.
5854      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5855      * for the request to the remote server.
5856      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5857      * object into a block of Roo.data.Records.
5858      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5859      * The function must be passed <ul>
5860      * <li>The Record block object</li>
5861      * <li>The "arg" argument from the load function</li>
5862      * <li>A boolean success indicator</li>
5863      * </ul>
5864      * @param {Object} scope The scope in which to call the callback
5865      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5866      */
5867     load : function(params, reader, callback, scope, arg){
5868         if(this.fireEvent("beforeload", this, params) !== false){
5869             var  o = {
5870                 params : params || {},
5871                 request: {
5872                     callback : callback,
5873                     scope : scope,
5874                     arg : arg
5875                 },
5876                 reader: reader,
5877                 callback : this.loadResponse,
5878                 scope: this
5879             };
5880             if(this.useAjax){
5881                 Roo.applyIf(o, this.conn);
5882                 if(this.activeRequest){
5883                     Roo.Ajax.abort(this.activeRequest);
5884                 }
5885                 this.activeRequest = Roo.Ajax.request(o);
5886             }else{
5887                 this.conn.request(o);
5888             }
5889         }else{
5890             callback.call(scope||this, null, arg, false);
5891         }
5892     },
5893
5894     // private
5895     loadResponse : function(o, success, response){
5896         delete this.activeRequest;
5897         if(!success){
5898             this.fireEvent("loadexception", this, o, response);
5899             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5900             return;
5901         }
5902         var result;
5903         try {
5904             result = o.reader.read(response);
5905         }catch(e){
5906             this.fireEvent("loadexception", this, o, response, e);
5907             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5908             return;
5909         }
5910         
5911         this.fireEvent("load", this, o, o.request.arg);
5912         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5913     },
5914
5915     // private
5916     update : function(dataSet){
5917
5918     },
5919
5920     // private
5921     updateResponse : function(dataSet){
5922
5923     }
5924 });/*
5925  * Based on:
5926  * Ext JS Library 1.1.1
5927  * Copyright(c) 2006-2007, Ext JS, LLC.
5928  *
5929  * Originally Released Under LGPL - original licence link has changed is not relivant.
5930  *
5931  * Fork - LGPL
5932  * <script type="text/javascript">
5933  */
5934
5935 /**
5936  * @class Roo.data.ScriptTagProxy
5937  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5938  * other than the originating domain of the running page.<br><br>
5939  * <p>
5940  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5941  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5942  * <p>
5943  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5944  * source code that is used as the source inside a &lt;script> tag.<br><br>
5945  * <p>
5946  * In order for the browser to process the returned data, the server must wrap the data object
5947  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5948  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5949  * depending on whether the callback name was passed:
5950  * <p>
5951  * <pre><code>
5952 boolean scriptTag = false;
5953 String cb = request.getParameter("callback");
5954 if (cb != null) {
5955     scriptTag = true;
5956     response.setContentType("text/javascript");
5957 } else {
5958     response.setContentType("application/x-json");
5959 }
5960 Writer out = response.getWriter();
5961 if (scriptTag) {
5962     out.write(cb + "(");
5963 }
5964 out.print(dataBlock.toJsonString());
5965 if (scriptTag) {
5966     out.write(");");
5967 }
5968 </pre></code>
5969  *
5970  * @constructor
5971  * @param {Object} config A configuration object.
5972  */
5973 Roo.data.ScriptTagProxy = function(config){
5974     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5975     Roo.apply(this, config);
5976     this.head = document.getElementsByTagName("head")[0];
5977 };
5978
5979 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5980
5981 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5982     /**
5983      * @cfg {String} url The URL from which to request the data object.
5984      */
5985     /**
5986      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5987      */
5988     timeout : 30000,
5989     /**
5990      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5991      * the server the name of the callback function set up by the load call to process the returned data object.
5992      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5993      * javascript output which calls this named function passing the data object as its only parameter.
5994      */
5995     callbackParam : "callback",
5996     /**
5997      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5998      * name to the request.
5999      */
6000     nocache : true,
6001
6002     /**
6003      * Load data from the configured URL, read the data object into
6004      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6005      * process that block using the passed callback.
6006      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6007      * for the request to the remote server.
6008      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6009      * object into a block of Roo.data.Records.
6010      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6011      * The function must be passed <ul>
6012      * <li>The Record block object</li>
6013      * <li>The "arg" argument from the load function</li>
6014      * <li>A boolean success indicator</li>
6015      * </ul>
6016      * @param {Object} scope The scope in which to call the callback
6017      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6018      */
6019     load : function(params, reader, callback, scope, arg){
6020         if(this.fireEvent("beforeload", this, params) !== false){
6021
6022             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6023
6024             var url = this.url;
6025             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6026             if(this.nocache){
6027                 url += "&_dc=" + (new Date().getTime());
6028             }
6029             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6030             var trans = {
6031                 id : transId,
6032                 cb : "stcCallback"+transId,
6033                 scriptId : "stcScript"+transId,
6034                 params : params,
6035                 arg : arg,
6036                 url : url,
6037                 callback : callback,
6038                 scope : scope,
6039                 reader : reader
6040             };
6041             var conn = this;
6042
6043             window[trans.cb] = function(o){
6044                 conn.handleResponse(o, trans);
6045             };
6046
6047             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6048
6049             if(this.autoAbort !== false){
6050                 this.abort();
6051             }
6052
6053             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6054
6055             var script = document.createElement("script");
6056             script.setAttribute("src", url);
6057             script.setAttribute("type", "text/javascript");
6058             script.setAttribute("id", trans.scriptId);
6059             this.head.appendChild(script);
6060
6061             this.trans = trans;
6062         }else{
6063             callback.call(scope||this, null, arg, false);
6064         }
6065     },
6066
6067     // private
6068     isLoading : function(){
6069         return this.trans ? true : false;
6070     },
6071
6072     /**
6073      * Abort the current server request.
6074      */
6075     abort : function(){
6076         if(this.isLoading()){
6077             this.destroyTrans(this.trans);
6078         }
6079     },
6080
6081     // private
6082     destroyTrans : function(trans, isLoaded){
6083         this.head.removeChild(document.getElementById(trans.scriptId));
6084         clearTimeout(trans.timeoutId);
6085         if(isLoaded){
6086             window[trans.cb] = undefined;
6087             try{
6088                 delete window[trans.cb];
6089             }catch(e){}
6090         }else{
6091             // if hasn't been loaded, wait for load to remove it to prevent script error
6092             window[trans.cb] = function(){
6093                 window[trans.cb] = undefined;
6094                 try{
6095                     delete window[trans.cb];
6096                 }catch(e){}
6097             };
6098         }
6099     },
6100
6101     // private
6102     handleResponse : function(o, trans){
6103         this.trans = false;
6104         this.destroyTrans(trans, true);
6105         var result;
6106         try {
6107             result = trans.reader.readRecords(o);
6108         }catch(e){
6109             this.fireEvent("loadexception", this, o, trans.arg, e);
6110             trans.callback.call(trans.scope||window, null, trans.arg, false);
6111             return;
6112         }
6113         this.fireEvent("load", this, o, trans.arg);
6114         trans.callback.call(trans.scope||window, result, trans.arg, true);
6115     },
6116
6117     // private
6118     handleFailure : function(trans){
6119         this.trans = false;
6120         this.destroyTrans(trans, false);
6121         this.fireEvent("loadexception", this, null, trans.arg);
6122         trans.callback.call(trans.scope||window, null, trans.arg, false);
6123     }
6124 });/*
6125  * Based on:
6126  * Ext JS Library 1.1.1
6127  * Copyright(c) 2006-2007, Ext JS, LLC.
6128  *
6129  * Originally Released Under LGPL - original licence link has changed is not relivant.
6130  *
6131  * Fork - LGPL
6132  * <script type="text/javascript">
6133  */
6134
6135 /**
6136  * @class Roo.data.JsonReader
6137  * @extends Roo.data.DataReader
6138  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6139  * based on mappings in a provided Roo.data.Record constructor.
6140  * 
6141  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6142  * in the reply previously. 
6143  * 
6144  * <p>
6145  * Example code:
6146  * <pre><code>
6147 var RecordDef = Roo.data.Record.create([
6148     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6149     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6150 ]);
6151 var myReader = new Roo.data.JsonReader({
6152     totalProperty: "results",    // The property which contains the total dataset size (optional)
6153     root: "rows",                // The property which contains an Array of row objects
6154     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6155 }, RecordDef);
6156 </code></pre>
6157  * <p>
6158  * This would consume a JSON file like this:
6159  * <pre><code>
6160 { 'results': 2, 'rows': [
6161     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6162     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6163 }
6164 </code></pre>
6165  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6166  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6167  * paged from the remote server.
6168  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6169  * @cfg {String} root name of the property which contains the Array of row objects.
6170  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6171  * @constructor
6172  * Create a new JsonReader
6173  * @param {Object} meta Metadata configuration options
6174  * @param {Object} recordType Either an Array of field definition objects,
6175  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6176  */
6177 Roo.data.JsonReader = function(meta, recordType){
6178     
6179     meta = meta || {};
6180     // set some defaults:
6181     Roo.applyIf(meta, {
6182         totalProperty: 'total',
6183         successProperty : 'success',
6184         root : 'data',
6185         id : 'id'
6186     });
6187     
6188     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6189 };
6190 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6191     
6192     /**
6193      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6194      * Used by Store query builder to append _requestMeta to params.
6195      * 
6196      */
6197     metaFromRemote : false,
6198     /**
6199      * This method is only used by a DataProxy which has retrieved data from a remote server.
6200      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6201      * @return {Object} data A data block which is used by an Roo.data.Store object as
6202      * a cache of Roo.data.Records.
6203      */
6204     read : function(response){
6205         var json = response.responseText;
6206        
6207         var o = /* eval:var:o */ eval("("+json+")");
6208         if(!o) {
6209             throw {message: "JsonReader.read: Json object not found"};
6210         }
6211         
6212         if(o.metaData){
6213             
6214             delete this.ef;
6215             this.metaFromRemote = true;
6216             this.meta = o.metaData;
6217             this.recordType = Roo.data.Record.create(o.metaData.fields);
6218             this.onMetaChange(this.meta, this.recordType, o);
6219         }
6220         return this.readRecords(o);
6221     },
6222
6223     // private function a store will implement
6224     onMetaChange : function(meta, recordType, o){
6225
6226     },
6227
6228     /**
6229          * @ignore
6230          */
6231     simpleAccess: function(obj, subsc) {
6232         return obj[subsc];
6233     },
6234
6235         /**
6236          * @ignore
6237          */
6238     getJsonAccessor: function(){
6239         var re = /[\[\.]/;
6240         return function(expr) {
6241             try {
6242                 return(re.test(expr))
6243                     ? new Function("obj", "return obj." + expr)
6244                     : function(obj){
6245                         return obj[expr];
6246                     };
6247             } catch(e){}
6248             return Roo.emptyFn;
6249         };
6250     }(),
6251
6252     /**
6253      * Create a data block containing Roo.data.Records from an XML document.
6254      * @param {Object} o An object which contains an Array of row objects in the property specified
6255      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6256      * which contains the total size of the dataset.
6257      * @return {Object} data A data block which is used by an Roo.data.Store object as
6258      * a cache of Roo.data.Records.
6259      */
6260     readRecords : function(o){
6261         /**
6262          * After any data loads, the raw JSON data is available for further custom processing.
6263          * @type Object
6264          */
6265         this.o = o;
6266         var s = this.meta, Record = this.recordType,
6267             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6268
6269 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6270         if (!this.ef) {
6271             if(s.totalProperty) {
6272                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6273                 }
6274                 if(s.successProperty) {
6275                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6276                 }
6277                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6278                 if (s.id) {
6279                         var g = this.getJsonAccessor(s.id);
6280                         this.getId = function(rec) {
6281                                 var r = g(rec);  
6282                                 return (r === undefined || r === "") ? null : r;
6283                         };
6284                 } else {
6285                         this.getId = function(){return null;};
6286                 }
6287             this.ef = [];
6288             for(var jj = 0; jj < fl; jj++){
6289                 f = fi[jj];
6290                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6291                 this.ef[jj] = this.getJsonAccessor(map);
6292             }
6293         }
6294
6295         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6296         if(s.totalProperty){
6297             var vt = parseInt(this.getTotal(o), 10);
6298             if(!isNaN(vt)){
6299                 totalRecords = vt;
6300             }
6301         }
6302         if(s.successProperty){
6303             var vs = this.getSuccess(o);
6304             if(vs === false || vs === 'false'){
6305                 success = false;
6306             }
6307         }
6308         var records = [];
6309         for(var i = 0; i < c; i++){
6310                 var n = root[i];
6311             var values = {};
6312             var id = this.getId(n);
6313             for(var j = 0; j < fl; j++){
6314                 f = fi[j];
6315             var v = this.ef[j](n);
6316             if (!f.convert) {
6317                 Roo.log('missing convert for ' + f.name);
6318                 Roo.log(f);
6319                 continue;
6320             }
6321             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6322             }
6323             var record = new Record(values, id);
6324             record.json = n;
6325             records[i] = record;
6326         }
6327         return {
6328             raw : o,
6329             success : success,
6330             records : records,
6331             totalRecords : totalRecords
6332         };
6333     }
6334 });/*
6335  * Based on:
6336  * Ext JS Library 1.1.1
6337  * Copyright(c) 2006-2007, Ext JS, LLC.
6338  *
6339  * Originally Released Under LGPL - original licence link has changed is not relivant.
6340  *
6341  * Fork - LGPL
6342  * <script type="text/javascript">
6343  */
6344
6345 /**
6346  * @class Roo.data.XmlReader
6347  * @extends Roo.data.DataReader
6348  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6349  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6350  * <p>
6351  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6352  * header in the HTTP response must be set to "text/xml".</em>
6353  * <p>
6354  * Example code:
6355  * <pre><code>
6356 var RecordDef = Roo.data.Record.create([
6357    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6358    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6359 ]);
6360 var myReader = new Roo.data.XmlReader({
6361    totalRecords: "results", // The element which contains the total dataset size (optional)
6362    record: "row",           // The repeated element which contains row information
6363    id: "id"                 // The element within the row that provides an ID for the record (optional)
6364 }, RecordDef);
6365 </code></pre>
6366  * <p>
6367  * This would consume an XML file like this:
6368  * <pre><code>
6369 &lt;?xml?>
6370 &lt;dataset>
6371  &lt;results>2&lt;/results>
6372  &lt;row>
6373    &lt;id>1&lt;/id>
6374    &lt;name>Bill&lt;/name>
6375    &lt;occupation>Gardener&lt;/occupation>
6376  &lt;/row>
6377  &lt;row>
6378    &lt;id>2&lt;/id>
6379    &lt;name>Ben&lt;/name>
6380    &lt;occupation>Horticulturalist&lt;/occupation>
6381  &lt;/row>
6382 &lt;/dataset>
6383 </code></pre>
6384  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6385  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6386  * paged from the remote server.
6387  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6388  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6389  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6390  * a record identifier value.
6391  * @constructor
6392  * Create a new XmlReader
6393  * @param {Object} meta Metadata configuration options
6394  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6395  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6396  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6397  */
6398 Roo.data.XmlReader = function(meta, recordType){
6399     meta = meta || {};
6400     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6401 };
6402 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6403     /**
6404      * This method is only used by a DataProxy which has retrieved data from a remote server.
6405          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6406          * to contain a method called 'responseXML' that returns an XML document object.
6407      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6408      * a cache of Roo.data.Records.
6409      */
6410     read : function(response){
6411         var doc = response.responseXML;
6412         if(!doc) {
6413             throw {message: "XmlReader.read: XML Document not available"};
6414         }
6415         return this.readRecords(doc);
6416     },
6417
6418     /**
6419      * Create a data block containing Roo.data.Records from an XML document.
6420          * @param {Object} doc A parsed XML document.
6421      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6422      * a cache of Roo.data.Records.
6423      */
6424     readRecords : function(doc){
6425         /**
6426          * After any data loads/reads, the raw XML Document is available for further custom processing.
6427          * @type XMLDocument
6428          */
6429         this.xmlData = doc;
6430         var root = doc.documentElement || doc;
6431         var q = Roo.DomQuery;
6432         var recordType = this.recordType, fields = recordType.prototype.fields;
6433         var sid = this.meta.id;
6434         var totalRecords = 0, success = true;
6435         if(this.meta.totalRecords){
6436             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6437         }
6438         
6439         if(this.meta.success){
6440             var sv = q.selectValue(this.meta.success, root, true);
6441             success = sv !== false && sv !== 'false';
6442         }
6443         var records = [];
6444         var ns = q.select(this.meta.record, root);
6445         for(var i = 0, len = ns.length; i < len; i++) {
6446                 var n = ns[i];
6447                 var values = {};
6448                 var id = sid ? q.selectValue(sid, n) : undefined;
6449                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6450                     var f = fields.items[j];
6451                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6452                     v = f.convert(v);
6453                     values[f.name] = v;
6454                 }
6455                 var record = new recordType(values, id);
6456                 record.node = n;
6457                 records[records.length] = record;
6458             }
6459
6460             return {
6461                 success : success,
6462                 records : records,
6463                 totalRecords : totalRecords || records.length
6464             };
6465     }
6466 });/*
6467  * Based on:
6468  * Ext JS Library 1.1.1
6469  * Copyright(c) 2006-2007, Ext JS, LLC.
6470  *
6471  * Originally Released Under LGPL - original licence link has changed is not relivant.
6472  *
6473  * Fork - LGPL
6474  * <script type="text/javascript">
6475  */
6476
6477 /**
6478  * @class Roo.data.ArrayReader
6479  * @extends Roo.data.DataReader
6480  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6481  * Each element of that Array represents a row of data fields. The
6482  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6483  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6484  * <p>
6485  * Example code:.
6486  * <pre><code>
6487 var RecordDef = Roo.data.Record.create([
6488     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6489     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6490 ]);
6491 var myReader = new Roo.data.ArrayReader({
6492     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6493 }, RecordDef);
6494 </code></pre>
6495  * <p>
6496  * This would consume an Array like this:
6497  * <pre><code>
6498 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6499   </code></pre>
6500  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6501  * @constructor
6502  * Create a new JsonReader
6503  * @param {Object} meta Metadata configuration options.
6504  * @param {Object} recordType Either an Array of field definition objects
6505  * as specified to {@link Roo.data.Record#create},
6506  * or an {@link Roo.data.Record} object
6507  * created using {@link Roo.data.Record#create}.
6508  */
6509 Roo.data.ArrayReader = function(meta, recordType){
6510     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6511 };
6512
6513 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6514     /**
6515      * Create a data block containing Roo.data.Records from an XML document.
6516      * @param {Object} o An Array of row objects which represents the dataset.
6517      * @return {Object} data A data block which is used by an Roo.data.Store object as
6518      * a cache of Roo.data.Records.
6519      */
6520     readRecords : function(o){
6521         var sid = this.meta ? this.meta.id : null;
6522         var recordType = this.recordType, fields = recordType.prototype.fields;
6523         var records = [];
6524         var root = o;
6525             for(var i = 0; i < root.length; i++){
6526                     var n = root[i];
6527                 var values = {};
6528                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6529                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6530                 var f = fields.items[j];
6531                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6532                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6533                 v = f.convert(v);
6534                 values[f.name] = v;
6535             }
6536                 var record = new recordType(values, id);
6537                 record.json = n;
6538                 records[records.length] = record;
6539             }
6540             return {
6541                 records : records,
6542                 totalRecords : records.length
6543             };
6544     }
6545 });/*
6546  * Based on:
6547  * Ext JS Library 1.1.1
6548  * Copyright(c) 2006-2007, Ext JS, LLC.
6549  *
6550  * Originally Released Under LGPL - original licence link has changed is not relivant.
6551  *
6552  * Fork - LGPL
6553  * <script type="text/javascript">
6554  */
6555
6556
6557 /**
6558  * @class Roo.data.Tree
6559  * @extends Roo.util.Observable
6560  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6561  * in the tree have most standard DOM functionality.
6562  * @constructor
6563  * @param {Node} root (optional) The root node
6564  */
6565 Roo.data.Tree = function(root){
6566    this.nodeHash = {};
6567    /**
6568     * The root node for this tree
6569     * @type Node
6570     */
6571    this.root = null;
6572    if(root){
6573        this.setRootNode(root);
6574    }
6575    this.addEvents({
6576        /**
6577         * @event append
6578         * Fires when a new child node is appended to a node in this tree.
6579         * @param {Tree} tree The owner tree
6580         * @param {Node} parent The parent node
6581         * @param {Node} node The newly appended node
6582         * @param {Number} index The index of the newly appended node
6583         */
6584        "append" : true,
6585        /**
6586         * @event remove
6587         * Fires when a child node is removed from a node in this tree.
6588         * @param {Tree} tree The owner tree
6589         * @param {Node} parent The parent node
6590         * @param {Node} node The child node removed
6591         */
6592        "remove" : true,
6593        /**
6594         * @event move
6595         * Fires when a node is moved to a new location in the tree
6596         * @param {Tree} tree The owner tree
6597         * @param {Node} node The node moved
6598         * @param {Node} oldParent The old parent of this node
6599         * @param {Node} newParent The new parent of this node
6600         * @param {Number} index The index it was moved to
6601         */
6602        "move" : true,
6603        /**
6604         * @event insert
6605         * Fires when a new child node is inserted in a node in this tree.
6606         * @param {Tree} tree The owner tree
6607         * @param {Node} parent The parent node
6608         * @param {Node} node The child node inserted
6609         * @param {Node} refNode The child node the node was inserted before
6610         */
6611        "insert" : true,
6612        /**
6613         * @event beforeappend
6614         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6615         * @param {Tree} tree The owner tree
6616         * @param {Node} parent The parent node
6617         * @param {Node} node The child node to be appended
6618         */
6619        "beforeappend" : true,
6620        /**
6621         * @event beforeremove
6622         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6623         * @param {Tree} tree The owner tree
6624         * @param {Node} parent The parent node
6625         * @param {Node} node The child node to be removed
6626         */
6627        "beforeremove" : true,
6628        /**
6629         * @event beforemove
6630         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6631         * @param {Tree} tree The owner tree
6632         * @param {Node} node The node being moved
6633         * @param {Node} oldParent The parent of the node
6634         * @param {Node} newParent The new parent the node is moving to
6635         * @param {Number} index The index it is being moved to
6636         */
6637        "beforemove" : true,
6638        /**
6639         * @event beforeinsert
6640         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6641         * @param {Tree} tree The owner tree
6642         * @param {Node} parent The parent node
6643         * @param {Node} node The child node to be inserted
6644         * @param {Node} refNode The child node the node is being inserted before
6645         */
6646        "beforeinsert" : true
6647    });
6648
6649     Roo.data.Tree.superclass.constructor.call(this);
6650 };
6651
6652 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6653     pathSeparator: "/",
6654
6655     proxyNodeEvent : function(){
6656         return this.fireEvent.apply(this, arguments);
6657     },
6658
6659     /**
6660      * Returns the root node for this tree.
6661      * @return {Node}
6662      */
6663     getRootNode : function(){
6664         return this.root;
6665     },
6666
6667     /**
6668      * Sets the root node for this tree.
6669      * @param {Node} node
6670      * @return {Node}
6671      */
6672     setRootNode : function(node){
6673         this.root = node;
6674         node.ownerTree = this;
6675         node.isRoot = true;
6676         this.registerNode(node);
6677         return node;
6678     },
6679
6680     /**
6681      * Gets a node in this tree by its id.
6682      * @param {String} id
6683      * @return {Node}
6684      */
6685     getNodeById : function(id){
6686         return this.nodeHash[id];
6687     },
6688
6689     registerNode : function(node){
6690         this.nodeHash[node.id] = node;
6691     },
6692
6693     unregisterNode : function(node){
6694         delete this.nodeHash[node.id];
6695     },
6696
6697     toString : function(){
6698         return "[Tree"+(this.id?" "+this.id:"")+"]";
6699     }
6700 });
6701
6702 /**
6703  * @class Roo.data.Node
6704  * @extends Roo.util.Observable
6705  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6706  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6707  * @constructor
6708  * @param {Object} attributes The attributes/config for the node
6709  */
6710 Roo.data.Node = function(attributes){
6711     /**
6712      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6713      * @type {Object}
6714      */
6715     this.attributes = attributes || {};
6716     this.leaf = this.attributes.leaf;
6717     /**
6718      * The node id. @type String
6719      */
6720     this.id = this.attributes.id;
6721     if(!this.id){
6722         this.id = Roo.id(null, "ynode-");
6723         this.attributes.id = this.id;
6724     }
6725      
6726     
6727     /**
6728      * All child nodes of this node. @type Array
6729      */
6730     this.childNodes = [];
6731     if(!this.childNodes.indexOf){ // indexOf is a must
6732         this.childNodes.indexOf = function(o){
6733             for(var i = 0, len = this.length; i < len; i++){
6734                 if(this[i] == o) {
6735                     return i;
6736                 }
6737             }
6738             return -1;
6739         };
6740     }
6741     /**
6742      * The parent node for this node. @type Node
6743      */
6744     this.parentNode = null;
6745     /**
6746      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6747      */
6748     this.firstChild = null;
6749     /**
6750      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6751      */
6752     this.lastChild = null;
6753     /**
6754      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6755      */
6756     this.previousSibling = null;
6757     /**
6758      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6759      */
6760     this.nextSibling = null;
6761
6762     this.addEvents({
6763        /**
6764         * @event append
6765         * Fires when a new child node is appended
6766         * @param {Tree} tree The owner tree
6767         * @param {Node} this This node
6768         * @param {Node} node The newly appended node
6769         * @param {Number} index The index of the newly appended node
6770         */
6771        "append" : true,
6772        /**
6773         * @event remove
6774         * Fires when a child node is removed
6775         * @param {Tree} tree The owner tree
6776         * @param {Node} this This node
6777         * @param {Node} node The removed node
6778         */
6779        "remove" : true,
6780        /**
6781         * @event move
6782         * Fires when this node is moved to a new location in the tree
6783         * @param {Tree} tree The owner tree
6784         * @param {Node} this This node
6785         * @param {Node} oldParent The old parent of this node
6786         * @param {Node} newParent The new parent of this node
6787         * @param {Number} index The index it was moved to
6788         */
6789        "move" : true,
6790        /**
6791         * @event insert
6792         * Fires when a new child node is inserted.
6793         * @param {Tree} tree The owner tree
6794         * @param {Node} this This node
6795         * @param {Node} node The child node inserted
6796         * @param {Node} refNode The child node the node was inserted before
6797         */
6798        "insert" : true,
6799        /**
6800         * @event beforeappend
6801         * Fires before a new child is appended, return false to cancel the append.
6802         * @param {Tree} tree The owner tree
6803         * @param {Node} this This node
6804         * @param {Node} node The child node to be appended
6805         */
6806        "beforeappend" : true,
6807        /**
6808         * @event beforeremove
6809         * Fires before a child is removed, return false to cancel the remove.
6810         * @param {Tree} tree The owner tree
6811         * @param {Node} this This node
6812         * @param {Node} node The child node to be removed
6813         */
6814        "beforeremove" : true,
6815        /**
6816         * @event beforemove
6817         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6818         * @param {Tree} tree The owner tree
6819         * @param {Node} this This node
6820         * @param {Node} oldParent The parent of this node
6821         * @param {Node} newParent The new parent this node is moving to
6822         * @param {Number} index The index it is being moved to
6823         */
6824        "beforemove" : true,
6825        /**
6826         * @event beforeinsert
6827         * Fires before a new child is inserted, return false to cancel the insert.
6828         * @param {Tree} tree The owner tree
6829         * @param {Node} this This node
6830         * @param {Node} node The child node to be inserted
6831         * @param {Node} refNode The child node the node is being inserted before
6832         */
6833        "beforeinsert" : true
6834    });
6835     this.listeners = this.attributes.listeners;
6836     Roo.data.Node.superclass.constructor.call(this);
6837 };
6838
6839 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6840     fireEvent : function(evtName){
6841         // first do standard event for this node
6842         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6843             return false;
6844         }
6845         // then bubble it up to the tree if the event wasn't cancelled
6846         var ot = this.getOwnerTree();
6847         if(ot){
6848             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6849                 return false;
6850             }
6851         }
6852         return true;
6853     },
6854
6855     /**
6856      * Returns true if this node is a leaf
6857      * @return {Boolean}
6858      */
6859     isLeaf : function(){
6860         return this.leaf === true;
6861     },
6862
6863     // private
6864     setFirstChild : function(node){
6865         this.firstChild = node;
6866     },
6867
6868     //private
6869     setLastChild : function(node){
6870         this.lastChild = node;
6871     },
6872
6873
6874     /**
6875      * Returns true if this node is the last child of its parent
6876      * @return {Boolean}
6877      */
6878     isLast : function(){
6879        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6880     },
6881
6882     /**
6883      * Returns true if this node is the first child of its parent
6884      * @return {Boolean}
6885      */
6886     isFirst : function(){
6887        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6888     },
6889
6890     hasChildNodes : function(){
6891         return !this.isLeaf() && this.childNodes.length > 0;
6892     },
6893
6894     /**
6895      * Insert node(s) as the last child node of this node.
6896      * @param {Node/Array} node The node or Array of nodes to append
6897      * @return {Node} The appended node if single append, or null if an array was passed
6898      */
6899     appendChild : function(node){
6900         var multi = false;
6901         if(node instanceof Array){
6902             multi = node;
6903         }else if(arguments.length > 1){
6904             multi = arguments;
6905         }
6906         // if passed an array or multiple args do them one by one
6907         if(multi){
6908             for(var i = 0, len = multi.length; i < len; i++) {
6909                 this.appendChild(multi[i]);
6910             }
6911         }else{
6912             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6913                 return false;
6914             }
6915             var index = this.childNodes.length;
6916             var oldParent = node.parentNode;
6917             // it's a move, make sure we move it cleanly
6918             if(oldParent){
6919                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6920                     return false;
6921                 }
6922                 oldParent.removeChild(node);
6923             }
6924             index = this.childNodes.length;
6925             if(index == 0){
6926                 this.setFirstChild(node);
6927             }
6928             this.childNodes.push(node);
6929             node.parentNode = this;
6930             var ps = this.childNodes[index-1];
6931             if(ps){
6932                 node.previousSibling = ps;
6933                 ps.nextSibling = node;
6934             }else{
6935                 node.previousSibling = null;
6936             }
6937             node.nextSibling = null;
6938             this.setLastChild(node);
6939             node.setOwnerTree(this.getOwnerTree());
6940             this.fireEvent("append", this.ownerTree, this, node, index);
6941             if(oldParent){
6942                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6943             }
6944             return node;
6945         }
6946     },
6947
6948     /**
6949      * Removes a child node from this node.
6950      * @param {Node} node The node to remove
6951      * @return {Node} The removed node
6952      */
6953     removeChild : function(node){
6954         var index = this.childNodes.indexOf(node);
6955         if(index == -1){
6956             return false;
6957         }
6958         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6959             return false;
6960         }
6961
6962         // remove it from childNodes collection
6963         this.childNodes.splice(index, 1);
6964
6965         // update siblings
6966         if(node.previousSibling){
6967             node.previousSibling.nextSibling = node.nextSibling;
6968         }
6969         if(node.nextSibling){
6970             node.nextSibling.previousSibling = node.previousSibling;
6971         }
6972
6973         // update child refs
6974         if(this.firstChild == node){
6975             this.setFirstChild(node.nextSibling);
6976         }
6977         if(this.lastChild == node){
6978             this.setLastChild(node.previousSibling);
6979         }
6980
6981         node.setOwnerTree(null);
6982         // clear any references from the node
6983         node.parentNode = null;
6984         node.previousSibling = null;
6985         node.nextSibling = null;
6986         this.fireEvent("remove", this.ownerTree, this, node);
6987         return node;
6988     },
6989
6990     /**
6991      * Inserts the first node before the second node in this nodes childNodes collection.
6992      * @param {Node} node The node to insert
6993      * @param {Node} refNode The node to insert before (if null the node is appended)
6994      * @return {Node} The inserted node
6995      */
6996     insertBefore : function(node, refNode){
6997         if(!refNode){ // like standard Dom, refNode can be null for append
6998             return this.appendChild(node);
6999         }
7000         // nothing to do
7001         if(node == refNode){
7002             return false;
7003         }
7004
7005         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7006             return false;
7007         }
7008         var index = this.childNodes.indexOf(refNode);
7009         var oldParent = node.parentNode;
7010         var refIndex = index;
7011
7012         // when moving internally, indexes will change after remove
7013         if(oldParent == this && this.childNodes.indexOf(node) < index){
7014             refIndex--;
7015         }
7016
7017         // it's a move, make sure we move it cleanly
7018         if(oldParent){
7019             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7020                 return false;
7021             }
7022             oldParent.removeChild(node);
7023         }
7024         if(refIndex == 0){
7025             this.setFirstChild(node);
7026         }
7027         this.childNodes.splice(refIndex, 0, node);
7028         node.parentNode = this;
7029         var ps = this.childNodes[refIndex-1];
7030         if(ps){
7031             node.previousSibling = ps;
7032             ps.nextSibling = node;
7033         }else{
7034             node.previousSibling = null;
7035         }
7036         node.nextSibling = refNode;
7037         refNode.previousSibling = node;
7038         node.setOwnerTree(this.getOwnerTree());
7039         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7040         if(oldParent){
7041             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7042         }
7043         return node;
7044     },
7045
7046     /**
7047      * Returns the child node at the specified index.
7048      * @param {Number} index
7049      * @return {Node}
7050      */
7051     item : function(index){
7052         return this.childNodes[index];
7053     },
7054
7055     /**
7056      * Replaces one child node in this node with another.
7057      * @param {Node} newChild The replacement node
7058      * @param {Node} oldChild The node to replace
7059      * @return {Node} The replaced node
7060      */
7061     replaceChild : function(newChild, oldChild){
7062         this.insertBefore(newChild, oldChild);
7063         this.removeChild(oldChild);
7064         return oldChild;
7065     },
7066
7067     /**
7068      * Returns the index of a child node
7069      * @param {Node} node
7070      * @return {Number} The index of the node or -1 if it was not found
7071      */
7072     indexOf : function(child){
7073         return this.childNodes.indexOf(child);
7074     },
7075
7076     /**
7077      * Returns the tree this node is in.
7078      * @return {Tree}
7079      */
7080     getOwnerTree : function(){
7081         // if it doesn't have one, look for one
7082         if(!this.ownerTree){
7083             var p = this;
7084             while(p){
7085                 if(p.ownerTree){
7086                     this.ownerTree = p.ownerTree;
7087                     break;
7088                 }
7089                 p = p.parentNode;
7090             }
7091         }
7092         return this.ownerTree;
7093     },
7094
7095     /**
7096      * Returns depth of this node (the root node has a depth of 0)
7097      * @return {Number}
7098      */
7099     getDepth : function(){
7100         var depth = 0;
7101         var p = this;
7102         while(p.parentNode){
7103             ++depth;
7104             p = p.parentNode;
7105         }
7106         return depth;
7107     },
7108
7109     // private
7110     setOwnerTree : function(tree){
7111         // if it's move, we need to update everyone
7112         if(tree != this.ownerTree){
7113             if(this.ownerTree){
7114                 this.ownerTree.unregisterNode(this);
7115             }
7116             this.ownerTree = tree;
7117             var cs = this.childNodes;
7118             for(var i = 0, len = cs.length; i < len; i++) {
7119                 cs[i].setOwnerTree(tree);
7120             }
7121             if(tree){
7122                 tree.registerNode(this);
7123             }
7124         }
7125     },
7126
7127     /**
7128      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7129      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7130      * @return {String} The path
7131      */
7132     getPath : function(attr){
7133         attr = attr || "id";
7134         var p = this.parentNode;
7135         var b = [this.attributes[attr]];
7136         while(p){
7137             b.unshift(p.attributes[attr]);
7138             p = p.parentNode;
7139         }
7140         var sep = this.getOwnerTree().pathSeparator;
7141         return sep + b.join(sep);
7142     },
7143
7144     /**
7145      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7146      * function call will be the scope provided or the current node. The arguments to the function
7147      * will be the args provided or the current node. If the function returns false at any point,
7148      * the bubble is stopped.
7149      * @param {Function} fn The function to call
7150      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7151      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7152      */
7153     bubble : function(fn, scope, args){
7154         var p = this;
7155         while(p){
7156             if(fn.call(scope || p, args || p) === false){
7157                 break;
7158             }
7159             p = p.parentNode;
7160         }
7161     },
7162
7163     /**
7164      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7165      * function call will be the scope provided or the current node. The arguments to the function
7166      * will be the args provided or the current node. If the function returns false at any point,
7167      * the cascade is stopped on that branch.
7168      * @param {Function} fn The function to call
7169      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7170      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7171      */
7172     cascade : function(fn, scope, args){
7173         if(fn.call(scope || this, args || this) !== false){
7174             var cs = this.childNodes;
7175             for(var i = 0, len = cs.length; i < len; i++) {
7176                 cs[i].cascade(fn, scope, args);
7177             }
7178         }
7179     },
7180
7181     /**
7182      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7183      * function call will be the scope provided or the current node. The arguments to the function
7184      * will be the args provided or the current node. If the function returns false at any point,
7185      * the iteration stops.
7186      * @param {Function} fn The function to call
7187      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7188      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7189      */
7190     eachChild : function(fn, scope, args){
7191         var cs = this.childNodes;
7192         for(var i = 0, len = cs.length; i < len; i++) {
7193                 if(fn.call(scope || this, args || cs[i]) === false){
7194                     break;
7195                 }
7196         }
7197     },
7198
7199     /**
7200      * Finds the first child that has the attribute with the specified value.
7201      * @param {String} attribute The attribute name
7202      * @param {Mixed} value The value to search for
7203      * @return {Node} The found child or null if none was found
7204      */
7205     findChild : function(attribute, value){
7206         var cs = this.childNodes;
7207         for(var i = 0, len = cs.length; i < len; i++) {
7208                 if(cs[i].attributes[attribute] == value){
7209                     return cs[i];
7210                 }
7211         }
7212         return null;
7213     },
7214
7215     /**
7216      * Finds the first child by a custom function. The child matches if the function passed
7217      * returns true.
7218      * @param {Function} fn
7219      * @param {Object} scope (optional)
7220      * @return {Node} The found child or null if none was found
7221      */
7222     findChildBy : function(fn, scope){
7223         var cs = this.childNodes;
7224         for(var i = 0, len = cs.length; i < len; i++) {
7225                 if(fn.call(scope||cs[i], cs[i]) === true){
7226                     return cs[i];
7227                 }
7228         }
7229         return null;
7230     },
7231
7232     /**
7233      * Sorts this nodes children using the supplied sort function
7234      * @param {Function} fn
7235      * @param {Object} scope (optional)
7236      */
7237     sort : function(fn, scope){
7238         var cs = this.childNodes;
7239         var len = cs.length;
7240         if(len > 0){
7241             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7242             cs.sort(sortFn);
7243             for(var i = 0; i < len; i++){
7244                 var n = cs[i];
7245                 n.previousSibling = cs[i-1];
7246                 n.nextSibling = cs[i+1];
7247                 if(i == 0){
7248                     this.setFirstChild(n);
7249                 }
7250                 if(i == len-1){
7251                     this.setLastChild(n);
7252                 }
7253             }
7254         }
7255     },
7256
7257     /**
7258      * Returns true if this node is an ancestor (at any point) of the passed node.
7259      * @param {Node} node
7260      * @return {Boolean}
7261      */
7262     contains : function(node){
7263         return node.isAncestor(this);
7264     },
7265
7266     /**
7267      * Returns true if the passed node is an ancestor (at any point) of this node.
7268      * @param {Node} node
7269      * @return {Boolean}
7270      */
7271     isAncestor : function(node){
7272         var p = this.parentNode;
7273         while(p){
7274             if(p == node){
7275                 return true;
7276             }
7277             p = p.parentNode;
7278         }
7279         return false;
7280     },
7281
7282     toString : function(){
7283         return "[Node"+(this.id?" "+this.id:"")+"]";
7284     }
7285 });/*
7286  * Based on:
7287  * Ext JS Library 1.1.1
7288  * Copyright(c) 2006-2007, Ext JS, LLC.
7289  *
7290  * Originally Released Under LGPL - original licence link has changed is not relivant.
7291  *
7292  * Fork - LGPL
7293  * <script type="text/javascript">
7294  */
7295  (function(){ 
7296 /**
7297  * @class Roo.Layer
7298  * @extends Roo.Element
7299  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7300  * automatic maintaining of shadow/shim positions.
7301  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7302  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7303  * you can pass a string with a CSS class name. False turns off the shadow.
7304  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7305  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7306  * @cfg {String} cls CSS class to add to the element
7307  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7308  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7309  * @constructor
7310  * @param {Object} config An object with config options.
7311  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7312  */
7313
7314 Roo.Layer = function(config, existingEl){
7315     config = config || {};
7316     var dh = Roo.DomHelper;
7317     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7318     if(existingEl){
7319         this.dom = Roo.getDom(existingEl);
7320     }
7321     if(!this.dom){
7322         var o = config.dh || {tag: "div", cls: "x-layer"};
7323         this.dom = dh.append(pel, o);
7324     }
7325     if(config.cls){
7326         this.addClass(config.cls);
7327     }
7328     this.constrain = config.constrain !== false;
7329     this.visibilityMode = Roo.Element.VISIBILITY;
7330     if(config.id){
7331         this.id = this.dom.id = config.id;
7332     }else{
7333         this.id = Roo.id(this.dom);
7334     }
7335     this.zindex = config.zindex || this.getZIndex();
7336     this.position("absolute", this.zindex);
7337     if(config.shadow){
7338         this.shadowOffset = config.shadowOffset || 4;
7339         this.shadow = new Roo.Shadow({
7340             offset : this.shadowOffset,
7341             mode : config.shadow
7342         });
7343     }else{
7344         this.shadowOffset = 0;
7345     }
7346     this.useShim = config.shim !== false && Roo.useShims;
7347     this.useDisplay = config.useDisplay;
7348     this.hide();
7349 };
7350
7351 var supr = Roo.Element.prototype;
7352
7353 // shims are shared among layer to keep from having 100 iframes
7354 var shims = [];
7355
7356 Roo.extend(Roo.Layer, Roo.Element, {
7357
7358     getZIndex : function(){
7359         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7360     },
7361
7362     getShim : function(){
7363         if(!this.useShim){
7364             return null;
7365         }
7366         if(this.shim){
7367             return this.shim;
7368         }
7369         var shim = shims.shift();
7370         if(!shim){
7371             shim = this.createShim();
7372             shim.enableDisplayMode('block');
7373             shim.dom.style.display = 'none';
7374             shim.dom.style.visibility = 'visible';
7375         }
7376         var pn = this.dom.parentNode;
7377         if(shim.dom.parentNode != pn){
7378             pn.insertBefore(shim.dom, this.dom);
7379         }
7380         shim.setStyle('z-index', this.getZIndex()-2);
7381         this.shim = shim;
7382         return shim;
7383     },
7384
7385     hideShim : function(){
7386         if(this.shim){
7387             this.shim.setDisplayed(false);
7388             shims.push(this.shim);
7389             delete this.shim;
7390         }
7391     },
7392
7393     disableShadow : function(){
7394         if(this.shadow){
7395             this.shadowDisabled = true;
7396             this.shadow.hide();
7397             this.lastShadowOffset = this.shadowOffset;
7398             this.shadowOffset = 0;
7399         }
7400     },
7401
7402     enableShadow : function(show){
7403         if(this.shadow){
7404             this.shadowDisabled = false;
7405             this.shadowOffset = this.lastShadowOffset;
7406             delete this.lastShadowOffset;
7407             if(show){
7408                 this.sync(true);
7409             }
7410         }
7411     },
7412
7413     // private
7414     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7415     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7416     sync : function(doShow){
7417         var sw = this.shadow;
7418         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7419             var sh = this.getShim();
7420
7421             var w = this.getWidth(),
7422                 h = this.getHeight();
7423
7424             var l = this.getLeft(true),
7425                 t = this.getTop(true);
7426
7427             if(sw && !this.shadowDisabled){
7428                 if(doShow && !sw.isVisible()){
7429                     sw.show(this);
7430                 }else{
7431                     sw.realign(l, t, w, h);
7432                 }
7433                 if(sh){
7434                     if(doShow){
7435                        sh.show();
7436                     }
7437                     // fit the shim behind the shadow, so it is shimmed too
7438                     var a = sw.adjusts, s = sh.dom.style;
7439                     s.left = (Math.min(l, l+a.l))+"px";
7440                     s.top = (Math.min(t, t+a.t))+"px";
7441                     s.width = (w+a.w)+"px";
7442                     s.height = (h+a.h)+"px";
7443                 }
7444             }else if(sh){
7445                 if(doShow){
7446                    sh.show();
7447                 }
7448                 sh.setSize(w, h);
7449                 sh.setLeftTop(l, t);
7450             }
7451             
7452         }
7453     },
7454
7455     // private
7456     destroy : function(){
7457         this.hideShim();
7458         if(this.shadow){
7459             this.shadow.hide();
7460         }
7461         this.removeAllListeners();
7462         var pn = this.dom.parentNode;
7463         if(pn){
7464             pn.removeChild(this.dom);
7465         }
7466         Roo.Element.uncache(this.id);
7467     },
7468
7469     remove : function(){
7470         this.destroy();
7471     },
7472
7473     // private
7474     beginUpdate : function(){
7475         this.updating = true;
7476     },
7477
7478     // private
7479     endUpdate : function(){
7480         this.updating = false;
7481         this.sync(true);
7482     },
7483
7484     // private
7485     hideUnders : function(negOffset){
7486         if(this.shadow){
7487             this.shadow.hide();
7488         }
7489         this.hideShim();
7490     },
7491
7492     // private
7493     constrainXY : function(){
7494         if(this.constrain){
7495             var vw = Roo.lib.Dom.getViewWidth(),
7496                 vh = Roo.lib.Dom.getViewHeight();
7497             var s = Roo.get(document).getScroll();
7498
7499             var xy = this.getXY();
7500             var x = xy[0], y = xy[1];   
7501             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7502             // only move it if it needs it
7503             var moved = false;
7504             // first validate right/bottom
7505             if((x + w) > vw+s.left){
7506                 x = vw - w - this.shadowOffset;
7507                 moved = true;
7508             }
7509             if((y + h) > vh+s.top){
7510                 y = vh - h - this.shadowOffset;
7511                 moved = true;
7512             }
7513             // then make sure top/left isn't negative
7514             if(x < s.left){
7515                 x = s.left;
7516                 moved = true;
7517             }
7518             if(y < s.top){
7519                 y = s.top;
7520                 moved = true;
7521             }
7522             if(moved){
7523                 if(this.avoidY){
7524                     var ay = this.avoidY;
7525                     if(y <= ay && (y+h) >= ay){
7526                         y = ay-h-5;   
7527                     }
7528                 }
7529                 xy = [x, y];
7530                 this.storeXY(xy);
7531                 supr.setXY.call(this, xy);
7532                 this.sync();
7533             }
7534         }
7535     },
7536
7537     isVisible : function(){
7538         return this.visible;    
7539     },
7540
7541     // private
7542     showAction : function(){
7543         this.visible = true; // track visibility to prevent getStyle calls
7544         if(this.useDisplay === true){
7545             this.setDisplayed("");
7546         }else if(this.lastXY){
7547             supr.setXY.call(this, this.lastXY);
7548         }else if(this.lastLT){
7549             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7550         }
7551     },
7552
7553     // private
7554     hideAction : function(){
7555         this.visible = false;
7556         if(this.useDisplay === true){
7557             this.setDisplayed(false);
7558         }else{
7559             this.setLeftTop(-10000,-10000);
7560         }
7561     },
7562
7563     // overridden Element method
7564     setVisible : function(v, a, d, c, e){
7565         if(v){
7566             this.showAction();
7567         }
7568         if(a && v){
7569             var cb = function(){
7570                 this.sync(true);
7571                 if(c){
7572                     c();
7573                 }
7574             }.createDelegate(this);
7575             supr.setVisible.call(this, true, true, d, cb, e);
7576         }else{
7577             if(!v){
7578                 this.hideUnders(true);
7579             }
7580             var cb = c;
7581             if(a){
7582                 cb = function(){
7583                     this.hideAction();
7584                     if(c){
7585                         c();
7586                     }
7587                 }.createDelegate(this);
7588             }
7589             supr.setVisible.call(this, v, a, d, cb, e);
7590             if(v){
7591                 this.sync(true);
7592             }else if(!a){
7593                 this.hideAction();
7594             }
7595         }
7596     },
7597
7598     storeXY : function(xy){
7599         delete this.lastLT;
7600         this.lastXY = xy;
7601     },
7602
7603     storeLeftTop : function(left, top){
7604         delete this.lastXY;
7605         this.lastLT = [left, top];
7606     },
7607
7608     // private
7609     beforeFx : function(){
7610         this.beforeAction();
7611         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7612     },
7613
7614     // private
7615     afterFx : function(){
7616         Roo.Layer.superclass.afterFx.apply(this, arguments);
7617         this.sync(this.isVisible());
7618     },
7619
7620     // private
7621     beforeAction : function(){
7622         if(!this.updating && this.shadow){
7623             this.shadow.hide();
7624         }
7625     },
7626
7627     // overridden Element method
7628     setLeft : function(left){
7629         this.storeLeftTop(left, this.getTop(true));
7630         supr.setLeft.apply(this, arguments);
7631         this.sync();
7632     },
7633
7634     setTop : function(top){
7635         this.storeLeftTop(this.getLeft(true), top);
7636         supr.setTop.apply(this, arguments);
7637         this.sync();
7638     },
7639
7640     setLeftTop : function(left, top){
7641         this.storeLeftTop(left, top);
7642         supr.setLeftTop.apply(this, arguments);
7643         this.sync();
7644     },
7645
7646     setXY : function(xy, a, d, c, e){
7647         this.fixDisplay();
7648         this.beforeAction();
7649         this.storeXY(xy);
7650         var cb = this.createCB(c);
7651         supr.setXY.call(this, xy, a, d, cb, e);
7652         if(!a){
7653             cb();
7654         }
7655     },
7656
7657     // private
7658     createCB : function(c){
7659         var el = this;
7660         return function(){
7661             el.constrainXY();
7662             el.sync(true);
7663             if(c){
7664                 c();
7665             }
7666         };
7667     },
7668
7669     // overridden Element method
7670     setX : function(x, a, d, c, e){
7671         this.setXY([x, this.getY()], a, d, c, e);
7672     },
7673
7674     // overridden Element method
7675     setY : function(y, a, d, c, e){
7676         this.setXY([this.getX(), y], a, d, c, e);
7677     },
7678
7679     // overridden Element method
7680     setSize : function(w, h, a, d, c, e){
7681         this.beforeAction();
7682         var cb = this.createCB(c);
7683         supr.setSize.call(this, w, h, a, d, cb, e);
7684         if(!a){
7685             cb();
7686         }
7687     },
7688
7689     // overridden Element method
7690     setWidth : function(w, a, d, c, e){
7691         this.beforeAction();
7692         var cb = this.createCB(c);
7693         supr.setWidth.call(this, w, a, d, cb, e);
7694         if(!a){
7695             cb();
7696         }
7697     },
7698
7699     // overridden Element method
7700     setHeight : function(h, a, d, c, e){
7701         this.beforeAction();
7702         var cb = this.createCB(c);
7703         supr.setHeight.call(this, h, a, d, cb, e);
7704         if(!a){
7705             cb();
7706         }
7707     },
7708
7709     // overridden Element method
7710     setBounds : function(x, y, w, h, a, d, c, e){
7711         this.beforeAction();
7712         var cb = this.createCB(c);
7713         if(!a){
7714             this.storeXY([x, y]);
7715             supr.setXY.call(this, [x, y]);
7716             supr.setSize.call(this, w, h, a, d, cb, e);
7717             cb();
7718         }else{
7719             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7720         }
7721         return this;
7722     },
7723     
7724     /**
7725      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7726      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7727      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7728      * @param {Number} zindex The new z-index to set
7729      * @return {this} The Layer
7730      */
7731     setZIndex : function(zindex){
7732         this.zindex = zindex;
7733         this.setStyle("z-index", zindex + 2);
7734         if(this.shadow){
7735             this.shadow.setZIndex(zindex + 1);
7736         }
7737         if(this.shim){
7738             this.shim.setStyle("z-index", zindex);
7739         }
7740     }
7741 });
7742 })();/*
7743  * Based on:
7744  * Ext JS Library 1.1.1
7745  * Copyright(c) 2006-2007, Ext JS, LLC.
7746  *
7747  * Originally Released Under LGPL - original licence link has changed is not relivant.
7748  *
7749  * Fork - LGPL
7750  * <script type="text/javascript">
7751  */
7752
7753
7754 /**
7755  * @class Roo.Shadow
7756  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7757  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7758  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7759  * @constructor
7760  * Create a new Shadow
7761  * @param {Object} config The config object
7762  */
7763 Roo.Shadow = function(config){
7764     Roo.apply(this, config);
7765     if(typeof this.mode != "string"){
7766         this.mode = this.defaultMode;
7767     }
7768     var o = this.offset, a = {h: 0};
7769     var rad = Math.floor(this.offset/2);
7770     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7771         case "drop":
7772             a.w = 0;
7773             a.l = a.t = o;
7774             a.t -= 1;
7775             if(Roo.isIE){
7776                 a.l -= this.offset + rad;
7777                 a.t -= this.offset + rad;
7778                 a.w -= rad;
7779                 a.h -= rad;
7780                 a.t += 1;
7781             }
7782         break;
7783         case "sides":
7784             a.w = (o*2);
7785             a.l = -o;
7786             a.t = o-1;
7787             if(Roo.isIE){
7788                 a.l -= (this.offset - rad);
7789                 a.t -= this.offset + rad;
7790                 a.l += 1;
7791                 a.w -= (this.offset - rad)*2;
7792                 a.w -= rad + 1;
7793                 a.h -= 1;
7794             }
7795         break;
7796         case "frame":
7797             a.w = a.h = (o*2);
7798             a.l = a.t = -o;
7799             a.t += 1;
7800             a.h -= 2;
7801             if(Roo.isIE){
7802                 a.l -= (this.offset - rad);
7803                 a.t -= (this.offset - rad);
7804                 a.l += 1;
7805                 a.w -= (this.offset + rad + 1);
7806                 a.h -= (this.offset + rad);
7807                 a.h += 1;
7808             }
7809         break;
7810     };
7811
7812     this.adjusts = a;
7813 };
7814
7815 Roo.Shadow.prototype = {
7816     /**
7817      * @cfg {String} mode
7818      * The shadow display mode.  Supports the following options:<br />
7819      * sides: Shadow displays on both sides and bottom only<br />
7820      * frame: Shadow displays equally on all four sides<br />
7821      * drop: Traditional bottom-right drop shadow (default)
7822      */
7823     /**
7824      * @cfg {String} offset
7825      * The number of pixels to offset the shadow from the element (defaults to 4)
7826      */
7827     offset: 4,
7828
7829     // private
7830     defaultMode: "drop",
7831
7832     /**
7833      * Displays the shadow under the target element
7834      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7835      */
7836     show : function(target){
7837         target = Roo.get(target);
7838         if(!this.el){
7839             this.el = Roo.Shadow.Pool.pull();
7840             if(this.el.dom.nextSibling != target.dom){
7841                 this.el.insertBefore(target);
7842             }
7843         }
7844         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7845         if(Roo.isIE){
7846             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7847         }
7848         this.realign(
7849             target.getLeft(true),
7850             target.getTop(true),
7851             target.getWidth(),
7852             target.getHeight()
7853         );
7854         this.el.dom.style.display = "block";
7855     },
7856
7857     /**
7858      * Returns true if the shadow is visible, else false
7859      */
7860     isVisible : function(){
7861         return this.el ? true : false;  
7862     },
7863
7864     /**
7865      * Direct alignment when values are already available. Show must be called at least once before
7866      * calling this method to ensure it is initialized.
7867      * @param {Number} left The target element left position
7868      * @param {Number} top The target element top position
7869      * @param {Number} width The target element width
7870      * @param {Number} height The target element height
7871      */
7872     realign : function(l, t, w, h){
7873         if(!this.el){
7874             return;
7875         }
7876         var a = this.adjusts, d = this.el.dom, s = d.style;
7877         var iea = 0;
7878         s.left = (l+a.l)+"px";
7879         s.top = (t+a.t)+"px";
7880         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7881  
7882         if(s.width != sws || s.height != shs){
7883             s.width = sws;
7884             s.height = shs;
7885             if(!Roo.isIE){
7886                 var cn = d.childNodes;
7887                 var sww = Math.max(0, (sw-12))+"px";
7888                 cn[0].childNodes[1].style.width = sww;
7889                 cn[1].childNodes[1].style.width = sww;
7890                 cn[2].childNodes[1].style.width = sww;
7891                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7892             }
7893         }
7894     },
7895
7896     /**
7897      * Hides this shadow
7898      */
7899     hide : function(){
7900         if(this.el){
7901             this.el.dom.style.display = "none";
7902             Roo.Shadow.Pool.push(this.el);
7903             delete this.el;
7904         }
7905     },
7906
7907     /**
7908      * Adjust the z-index of this shadow
7909      * @param {Number} zindex The new z-index
7910      */
7911     setZIndex : function(z){
7912         this.zIndex = z;
7913         if(this.el){
7914             this.el.setStyle("z-index", z);
7915         }
7916     }
7917 };
7918
7919 // Private utility class that manages the internal Shadow cache
7920 Roo.Shadow.Pool = function(){
7921     var p = [];
7922     var markup = Roo.isIE ?
7923                  '<div class="x-ie-shadow"></div>' :
7924                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
7925     return {
7926         pull : function(){
7927             var sh = p.shift();
7928             if(!sh){
7929                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7930                 sh.autoBoxAdjust = false;
7931             }
7932             return sh;
7933         },
7934
7935         push : function(sh){
7936             p.push(sh);
7937         }
7938     };
7939 }();/*
7940  * Based on:
7941  * Ext JS Library 1.1.1
7942  * Copyright(c) 2006-2007, Ext JS, LLC.
7943  *
7944  * Originally Released Under LGPL - original licence link has changed is not relivant.
7945  *
7946  * Fork - LGPL
7947  * <script type="text/javascript">
7948  */
7949
7950
7951 /**
7952  * @class Roo.SplitBar
7953  * @extends Roo.util.Observable
7954  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7955  * <br><br>
7956  * Usage:
7957  * <pre><code>
7958 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7959                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7960 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7961 split.minSize = 100;
7962 split.maxSize = 600;
7963 split.animate = true;
7964 split.on('moved', splitterMoved);
7965 </code></pre>
7966  * @constructor
7967  * Create a new SplitBar
7968  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
7969  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
7970  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7971  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
7972                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7973                         position of the SplitBar).
7974  */
7975 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7976     
7977     /** @private */
7978     this.el = Roo.get(dragElement, true);
7979     this.el.dom.unselectable = "on";
7980     /** @private */
7981     this.resizingEl = Roo.get(resizingElement, true);
7982
7983     /**
7984      * @private
7985      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7986      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7987      * @type Number
7988      */
7989     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7990     
7991     /**
7992      * The minimum size of the resizing element. (Defaults to 0)
7993      * @type Number
7994      */
7995     this.minSize = 0;
7996     
7997     /**
7998      * The maximum size of the resizing element. (Defaults to 2000)
7999      * @type Number
8000      */
8001     this.maxSize = 2000;
8002     
8003     /**
8004      * Whether to animate the transition to the new size
8005      * @type Boolean
8006      */
8007     this.animate = false;
8008     
8009     /**
8010      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8011      * @type Boolean
8012      */
8013     this.useShim = false;
8014     
8015     /** @private */
8016     this.shim = null;
8017     
8018     if(!existingProxy){
8019         /** @private */
8020         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8021     }else{
8022         this.proxy = Roo.get(existingProxy).dom;
8023     }
8024     /** @private */
8025     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8026     
8027     /** @private */
8028     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8029     
8030     /** @private */
8031     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8032     
8033     /** @private */
8034     this.dragSpecs = {};
8035     
8036     /**
8037      * @private The adapter to use to positon and resize elements
8038      */
8039     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8040     this.adapter.init(this);
8041     
8042     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8043         /** @private */
8044         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8045         this.el.addClass("x-splitbar-h");
8046     }else{
8047         /** @private */
8048         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8049         this.el.addClass("x-splitbar-v");
8050     }
8051     
8052     this.addEvents({
8053         /**
8054          * @event resize
8055          * Fires when the splitter is moved (alias for {@link #event-moved})
8056          * @param {Roo.SplitBar} this
8057          * @param {Number} newSize the new width or height
8058          */
8059         "resize" : true,
8060         /**
8061          * @event moved
8062          * Fires when the splitter is moved
8063          * @param {Roo.SplitBar} this
8064          * @param {Number} newSize the new width or height
8065          */
8066         "moved" : true,
8067         /**
8068          * @event beforeresize
8069          * Fires before the splitter is dragged
8070          * @param {Roo.SplitBar} this
8071          */
8072         "beforeresize" : true,
8073
8074         "beforeapply" : true
8075     });
8076
8077     Roo.util.Observable.call(this);
8078 };
8079
8080 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8081     onStartProxyDrag : function(x, y){
8082         this.fireEvent("beforeresize", this);
8083         if(!this.overlay){
8084             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8085             o.unselectable();
8086             o.enableDisplayMode("block");
8087             // all splitbars share the same overlay
8088             Roo.SplitBar.prototype.overlay = o;
8089         }
8090         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8091         this.overlay.show();
8092         Roo.get(this.proxy).setDisplayed("block");
8093         var size = this.adapter.getElementSize(this);
8094         this.activeMinSize = this.getMinimumSize();;
8095         this.activeMaxSize = this.getMaximumSize();;
8096         var c1 = size - this.activeMinSize;
8097         var c2 = Math.max(this.activeMaxSize - size, 0);
8098         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8099             this.dd.resetConstraints();
8100             this.dd.setXConstraint(
8101                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8102                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8103             );
8104             this.dd.setYConstraint(0, 0);
8105         }else{
8106             this.dd.resetConstraints();
8107             this.dd.setXConstraint(0, 0);
8108             this.dd.setYConstraint(
8109                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8110                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8111             );
8112          }
8113         this.dragSpecs.startSize = size;
8114         this.dragSpecs.startPoint = [x, y];
8115         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8116     },
8117     
8118     /** 
8119      * @private Called after the drag operation by the DDProxy
8120      */
8121     onEndProxyDrag : function(e){
8122         Roo.get(this.proxy).setDisplayed(false);
8123         var endPoint = Roo.lib.Event.getXY(e);
8124         if(this.overlay){
8125             this.overlay.hide();
8126         }
8127         var newSize;
8128         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8129             newSize = this.dragSpecs.startSize + 
8130                 (this.placement == Roo.SplitBar.LEFT ?
8131                     endPoint[0] - this.dragSpecs.startPoint[0] :
8132                     this.dragSpecs.startPoint[0] - endPoint[0]
8133                 );
8134         }else{
8135             newSize = this.dragSpecs.startSize + 
8136                 (this.placement == Roo.SplitBar.TOP ?
8137                     endPoint[1] - this.dragSpecs.startPoint[1] :
8138                     this.dragSpecs.startPoint[1] - endPoint[1]
8139                 );
8140         }
8141         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8142         if(newSize != this.dragSpecs.startSize){
8143             if(this.fireEvent('beforeapply', this, newSize) !== false){
8144                 this.adapter.setElementSize(this, newSize);
8145                 this.fireEvent("moved", this, newSize);
8146                 this.fireEvent("resize", this, newSize);
8147             }
8148         }
8149     },
8150     
8151     /**
8152      * Get the adapter this SplitBar uses
8153      * @return The adapter object
8154      */
8155     getAdapter : function(){
8156         return this.adapter;
8157     },
8158     
8159     /**
8160      * Set the adapter this SplitBar uses
8161      * @param {Object} adapter A SplitBar adapter object
8162      */
8163     setAdapter : function(adapter){
8164         this.adapter = adapter;
8165         this.adapter.init(this);
8166     },
8167     
8168     /**
8169      * Gets the minimum size for the resizing element
8170      * @return {Number} The minimum size
8171      */
8172     getMinimumSize : function(){
8173         return this.minSize;
8174     },
8175     
8176     /**
8177      * Sets the minimum size for the resizing element
8178      * @param {Number} minSize The minimum size
8179      */
8180     setMinimumSize : function(minSize){
8181         this.minSize = minSize;
8182     },
8183     
8184     /**
8185      * Gets the maximum size for the resizing element
8186      * @return {Number} The maximum size
8187      */
8188     getMaximumSize : function(){
8189         return this.maxSize;
8190     },
8191     
8192     /**
8193      * Sets the maximum size for the resizing element
8194      * @param {Number} maxSize The maximum size
8195      */
8196     setMaximumSize : function(maxSize){
8197         this.maxSize = maxSize;
8198     },
8199     
8200     /**
8201      * Sets the initialize size for the resizing element
8202      * @param {Number} size The initial size
8203      */
8204     setCurrentSize : function(size){
8205         var oldAnimate = this.animate;
8206         this.animate = false;
8207         this.adapter.setElementSize(this, size);
8208         this.animate = oldAnimate;
8209     },
8210     
8211     /**
8212      * Destroy this splitbar. 
8213      * @param {Boolean} removeEl True to remove the element
8214      */
8215     destroy : function(removeEl){
8216         if(this.shim){
8217             this.shim.remove();
8218         }
8219         this.dd.unreg();
8220         this.proxy.parentNode.removeChild(this.proxy);
8221         if(removeEl){
8222             this.el.remove();
8223         }
8224     }
8225 });
8226
8227 /**
8228  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8229  */
8230 Roo.SplitBar.createProxy = function(dir){
8231     var proxy = new Roo.Element(document.createElement("div"));
8232     proxy.unselectable();
8233     var cls = 'x-splitbar-proxy';
8234     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8235     document.body.appendChild(proxy.dom);
8236     return proxy.dom;
8237 };
8238
8239 /** 
8240  * @class Roo.SplitBar.BasicLayoutAdapter
8241  * Default Adapter. It assumes the splitter and resizing element are not positioned
8242  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8243  */
8244 Roo.SplitBar.BasicLayoutAdapter = function(){
8245 };
8246
8247 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8248     // do nothing for now
8249     init : function(s){
8250     
8251     },
8252     /**
8253      * Called before drag operations to get the current size of the resizing element. 
8254      * @param {Roo.SplitBar} s The SplitBar using this adapter
8255      */
8256      getElementSize : function(s){
8257         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8258             return s.resizingEl.getWidth();
8259         }else{
8260             return s.resizingEl.getHeight();
8261         }
8262     },
8263     
8264     /**
8265      * Called after drag operations to set the size of the resizing element.
8266      * @param {Roo.SplitBar} s The SplitBar using this adapter
8267      * @param {Number} newSize The new size to set
8268      * @param {Function} onComplete A function to be invoked when resizing is complete
8269      */
8270     setElementSize : function(s, newSize, onComplete){
8271         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8272             if(!s.animate){
8273                 s.resizingEl.setWidth(newSize);
8274                 if(onComplete){
8275                     onComplete(s, newSize);
8276                 }
8277             }else{
8278                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8279             }
8280         }else{
8281             
8282             if(!s.animate){
8283                 s.resizingEl.setHeight(newSize);
8284                 if(onComplete){
8285                     onComplete(s, newSize);
8286                 }
8287             }else{
8288                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8289             }
8290         }
8291     }
8292 };
8293
8294 /** 
8295  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8296  * @extends Roo.SplitBar.BasicLayoutAdapter
8297  * Adapter that  moves the splitter element to align with the resized sizing element. 
8298  * Used with an absolute positioned SplitBar.
8299  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8300  * document.body, make sure you assign an id to the body element.
8301  */
8302 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8303     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8304     this.container = Roo.get(container);
8305 };
8306
8307 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8308     init : function(s){
8309         this.basic.init(s);
8310     },
8311     
8312     getElementSize : function(s){
8313         return this.basic.getElementSize(s);
8314     },
8315     
8316     setElementSize : function(s, newSize, onComplete){
8317         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8318     },
8319     
8320     moveSplitter : function(s){
8321         var yes = Roo.SplitBar;
8322         switch(s.placement){
8323             case yes.LEFT:
8324                 s.el.setX(s.resizingEl.getRight());
8325                 break;
8326             case yes.RIGHT:
8327                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8328                 break;
8329             case yes.TOP:
8330                 s.el.setY(s.resizingEl.getBottom());
8331                 break;
8332             case yes.BOTTOM:
8333                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8334                 break;
8335         }
8336     }
8337 };
8338
8339 /**
8340  * Orientation constant - Create a vertical SplitBar
8341  * @static
8342  * @type Number
8343  */
8344 Roo.SplitBar.VERTICAL = 1;
8345
8346 /**
8347  * Orientation constant - Create a horizontal SplitBar
8348  * @static
8349  * @type Number
8350  */
8351 Roo.SplitBar.HORIZONTAL = 2;
8352
8353 /**
8354  * Placement constant - The resizing element is to the left of the splitter element
8355  * @static
8356  * @type Number
8357  */
8358 Roo.SplitBar.LEFT = 1;
8359
8360 /**
8361  * Placement constant - The resizing element is to the right of the splitter element
8362  * @static
8363  * @type Number
8364  */
8365 Roo.SplitBar.RIGHT = 2;
8366
8367 /**
8368  * Placement constant - The resizing element is positioned above the splitter element
8369  * @static
8370  * @type Number
8371  */
8372 Roo.SplitBar.TOP = 3;
8373
8374 /**
8375  * Placement constant - The resizing element is positioned under splitter element
8376  * @static
8377  * @type Number
8378  */
8379 Roo.SplitBar.BOTTOM = 4;
8380 /*
8381  * Based on:
8382  * Ext JS Library 1.1.1
8383  * Copyright(c) 2006-2007, Ext JS, LLC.
8384  *
8385  * Originally Released Under LGPL - original licence link has changed is not relivant.
8386  *
8387  * Fork - LGPL
8388  * <script type="text/javascript">
8389  */
8390
8391 /**
8392  * @class Roo.View
8393  * @extends Roo.util.Observable
8394  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8395  * This class also supports single and multi selection modes. <br>
8396  * Create a data model bound view:
8397  <pre><code>
8398  var store = new Roo.data.Store(...);
8399
8400  var view = new Roo.View({
8401     el : "my-element",
8402     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8403  
8404     singleSelect: true,
8405     selectedClass: "ydataview-selected",
8406     store: store
8407  });
8408
8409  // listen for node click?
8410  view.on("click", function(vw, index, node, e){
8411  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8412  });
8413
8414  // load XML data
8415  dataModel.load("foobar.xml");
8416  </code></pre>
8417  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8418  * <br><br>
8419  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8420  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8421  * 
8422  * Note: old style constructor is still suported (container, template, config)
8423  * 
8424  * @constructor
8425  * Create a new View
8426  * @param {Object} config The config object
8427  * 
8428  */
8429 Roo.View = function(config, depreciated_tpl, depreciated_config){
8430     
8431     this.parent = false;
8432     
8433     if (typeof(depreciated_tpl) == 'undefined') {
8434         // new way.. - universal constructor.
8435         Roo.apply(this, config);
8436         this.el  = Roo.get(this.el);
8437     } else {
8438         // old format..
8439         this.el  = Roo.get(config);
8440         this.tpl = depreciated_tpl;
8441         Roo.apply(this, depreciated_config);
8442     }
8443     this.wrapEl  = this.el.wrap().wrap();
8444     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8445     
8446     
8447     if(typeof(this.tpl) == "string"){
8448         this.tpl = new Roo.Template(this.tpl);
8449     } else {
8450         // support xtype ctors..
8451         this.tpl = new Roo.factory(this.tpl, Roo);
8452     }
8453     
8454     
8455     this.tpl.compile();
8456     
8457     /** @private */
8458     this.addEvents({
8459         /**
8460          * @event beforeclick
8461          * Fires before a click is processed. Returns false to cancel the default action.
8462          * @param {Roo.View} this
8463          * @param {Number} index The index of the target node
8464          * @param {HTMLElement} node The target node
8465          * @param {Roo.EventObject} e The raw event object
8466          */
8467             "beforeclick" : true,
8468         /**
8469          * @event click
8470          * Fires when a template node is clicked.
8471          * @param {Roo.View} this
8472          * @param {Number} index The index of the target node
8473          * @param {HTMLElement} node The target node
8474          * @param {Roo.EventObject} e The raw event object
8475          */
8476             "click" : true,
8477         /**
8478          * @event dblclick
8479          * Fires when a template node is double clicked.
8480          * @param {Roo.View} this
8481          * @param {Number} index The index of the target node
8482          * @param {HTMLElement} node The target node
8483          * @param {Roo.EventObject} e The raw event object
8484          */
8485             "dblclick" : true,
8486         /**
8487          * @event contextmenu
8488          * Fires when a template node is right clicked.
8489          * @param {Roo.View} this
8490          * @param {Number} index The index of the target node
8491          * @param {HTMLElement} node The target node
8492          * @param {Roo.EventObject} e The raw event object
8493          */
8494             "contextmenu" : true,
8495         /**
8496          * @event selectionchange
8497          * Fires when the selected nodes change.
8498          * @param {Roo.View} this
8499          * @param {Array} selections Array of the selected nodes
8500          */
8501             "selectionchange" : true,
8502     
8503         /**
8504          * @event beforeselect
8505          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8506          * @param {Roo.View} this
8507          * @param {HTMLElement} node The node to be selected
8508          * @param {Array} selections Array of currently selected nodes
8509          */
8510             "beforeselect" : true,
8511         /**
8512          * @event preparedata
8513          * Fires on every row to render, to allow you to change the data.
8514          * @param {Roo.View} this
8515          * @param {Object} data to be rendered (change this)
8516          */
8517           "preparedata" : true
8518           
8519           
8520         });
8521
8522
8523
8524     this.el.on({
8525         "click": this.onClick,
8526         "dblclick": this.onDblClick,
8527         "contextmenu": this.onContextMenu,
8528         scope:this
8529     });
8530
8531     this.selections = [];
8532     this.nodes = [];
8533     this.cmp = new Roo.CompositeElementLite([]);
8534     if(this.store){
8535         this.store = Roo.factory(this.store, Roo.data);
8536         this.setStore(this.store, true);
8537     }
8538     
8539     if ( this.footer && this.footer.xtype) {
8540            
8541          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8542         
8543         this.footer.dataSource = this.store
8544         this.footer.container = fctr;
8545         this.footer = Roo.factory(this.footer, Roo);
8546         fctr.insertFirst(this.el);
8547         
8548         // this is a bit insane - as the paging toolbar seems to detach the el..
8549 //        dom.parentNode.parentNode.parentNode
8550          // they get detached?
8551     }
8552     
8553     
8554     Roo.View.superclass.constructor.call(this);
8555     
8556     
8557 };
8558
8559 Roo.extend(Roo.View, Roo.util.Observable, {
8560     
8561      /**
8562      * @cfg {Roo.data.Store} store Data store to load data from.
8563      */
8564     store : false,
8565     
8566     /**
8567      * @cfg {String|Roo.Element} el The container element.
8568      */
8569     el : '',
8570     
8571     /**
8572      * @cfg {String|Roo.Template} tpl The template used by this View 
8573      */
8574     tpl : false,
8575     /**
8576      * @cfg {String} dataName the named area of the template to use as the data area
8577      *                          Works with domtemplates roo-name="name"
8578      */
8579     dataName: false,
8580     /**
8581      * @cfg {String} selectedClass The css class to add to selected nodes
8582      */
8583     selectedClass : "x-view-selected",
8584      /**
8585      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8586      */
8587     emptyText : "",
8588     
8589     /**
8590      * @cfg {String} text to display on mask (default Loading)
8591      */
8592     mask : false,
8593     /**
8594      * @cfg {Boolean} multiSelect Allow multiple selection
8595      */
8596     multiSelect : false,
8597     /**
8598      * @cfg {Boolean} singleSelect Allow single selection
8599      */
8600     singleSelect:  false,
8601     
8602     /**
8603      * @cfg {Boolean} toggleSelect - selecting 
8604      */
8605     toggleSelect : false,
8606     
8607     /**
8608      * @cfg {Boolean} tickable - selecting 
8609      */
8610     tickable : false,
8611     
8612     /**
8613      * Returns the element this view is bound to.
8614      * @return {Roo.Element}
8615      */
8616     getEl : function(){
8617         return this.wrapEl;
8618     },
8619     
8620     
8621
8622     /**
8623      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8624      */
8625     refresh : function(){
8626         Roo.log('refresh');
8627         var t = this.tpl;
8628         
8629         // if we are using something like 'domtemplate', then
8630         // the what gets used is:
8631         // t.applySubtemplate(NAME, data, wrapping data..)
8632         // the outer template then get' applied with
8633         //     the store 'extra data'
8634         // and the body get's added to the
8635         //      roo-name="data" node?
8636         //      <span class='roo-tpl-{name}'></span> ?????
8637         
8638         
8639         
8640         this.clearSelections();
8641         this.el.update("");
8642         var html = [];
8643         var records = this.store.getRange();
8644         if(records.length < 1) {
8645             
8646             // is this valid??  = should it render a template??
8647             
8648             this.el.update(this.emptyText);
8649             return;
8650         }
8651         var el = this.el;
8652         if (this.dataName) {
8653             this.el.update(t.apply(this.store.meta)); //????
8654             el = this.el.child('.roo-tpl-' + this.dataName);
8655         }
8656         
8657         for(var i = 0, len = records.length; i < len; i++){
8658             var data = this.prepareData(records[i].data, i, records[i]);
8659             this.fireEvent("preparedata", this, data, i, records[i]);
8660             
8661             var d = Roo.apply({}, data);
8662             
8663             if(this.tickable){
8664                 Roo.apply(d, {'roo-id' : Roo.id()});
8665                 
8666                 var _this = this;
8667             
8668                 Roo.each(this.parent.item, function(item){
8669                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8670                         return;
8671                     }
8672                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8673                 });
8674             }
8675             
8676             html[html.length] = Roo.util.Format.trim(
8677                 this.dataName ?
8678                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8679                     t.apply(d)
8680             );
8681         }
8682         
8683         
8684         
8685         el.update(html.join(""));
8686         this.nodes = el.dom.childNodes;
8687         this.updateIndexes(0);
8688     },
8689     
8690
8691     /**
8692      * Function to override to reformat the data that is sent to
8693      * the template for each node.
8694      * DEPRICATED - use the preparedata event handler.
8695      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8696      * a JSON object for an UpdateManager bound view).
8697      */
8698     prepareData : function(data, index, record)
8699     {
8700         this.fireEvent("preparedata", this, data, index, record);
8701         return data;
8702     },
8703
8704     onUpdate : function(ds, record){
8705          Roo.log('on update');   
8706         this.clearSelections();
8707         var index = this.store.indexOf(record);
8708         var n = this.nodes[index];
8709         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8710         n.parentNode.removeChild(n);
8711         this.updateIndexes(index, index);
8712     },
8713
8714     
8715     
8716 // --------- FIXME     
8717     onAdd : function(ds, records, index)
8718     {
8719         Roo.log(['on Add', ds, records, index] );        
8720         this.clearSelections();
8721         if(this.nodes.length == 0){
8722             this.refresh();
8723             return;
8724         }
8725         var n = this.nodes[index];
8726         for(var i = 0, len = records.length; i < len; i++){
8727             var d = this.prepareData(records[i].data, i, records[i]);
8728             if(n){
8729                 this.tpl.insertBefore(n, d);
8730             }else{
8731                 
8732                 this.tpl.append(this.el, d);
8733             }
8734         }
8735         this.updateIndexes(index);
8736     },
8737
8738     onRemove : function(ds, record, index){
8739         Roo.log('onRemove');
8740         this.clearSelections();
8741         var el = this.dataName  ?
8742             this.el.child('.roo-tpl-' + this.dataName) :
8743             this.el; 
8744         
8745         el.dom.removeChild(this.nodes[index]);
8746         this.updateIndexes(index);
8747     },
8748
8749     /**
8750      * Refresh an individual node.
8751      * @param {Number} index
8752      */
8753     refreshNode : function(index){
8754         this.onUpdate(this.store, this.store.getAt(index));
8755     },
8756
8757     updateIndexes : function(startIndex, endIndex){
8758         var ns = this.nodes;
8759         startIndex = startIndex || 0;
8760         endIndex = endIndex || ns.length - 1;
8761         for(var i = startIndex; i <= endIndex; i++){
8762             ns[i].nodeIndex = i;
8763         }
8764     },
8765
8766     /**
8767      * Changes the data store this view uses and refresh the view.
8768      * @param {Store} store
8769      */
8770     setStore : function(store, initial){
8771         if(!initial && this.store){
8772             this.store.un("datachanged", this.refresh);
8773             this.store.un("add", this.onAdd);
8774             this.store.un("remove", this.onRemove);
8775             this.store.un("update", this.onUpdate);
8776             this.store.un("clear", this.refresh);
8777             this.store.un("beforeload", this.onBeforeLoad);
8778             this.store.un("load", this.onLoad);
8779             this.store.un("loadexception", this.onLoad);
8780         }
8781         if(store){
8782           
8783             store.on("datachanged", this.refresh, this);
8784             store.on("add", this.onAdd, this);
8785             store.on("remove", this.onRemove, this);
8786             store.on("update", this.onUpdate, this);
8787             store.on("clear", this.refresh, this);
8788             store.on("beforeload", this.onBeforeLoad, this);
8789             store.on("load", this.onLoad, this);
8790             store.on("loadexception", this.onLoad, this);
8791         }
8792         
8793         if(store){
8794             this.refresh();
8795         }
8796     },
8797     /**
8798      * onbeforeLoad - masks the loading area.
8799      *
8800      */
8801     onBeforeLoad : function(store,opts)
8802     {
8803          Roo.log('onBeforeLoad');   
8804         if (!opts.add) {
8805             this.el.update("");
8806         }
8807         this.el.mask(this.mask ? this.mask : "Loading" ); 
8808     },
8809     onLoad : function ()
8810     {
8811         this.el.unmask();
8812     },
8813     
8814
8815     /**
8816      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8817      * @param {HTMLElement} node
8818      * @return {HTMLElement} The template node
8819      */
8820     findItemFromChild : function(node){
8821         var el = this.dataName  ?
8822             this.el.child('.roo-tpl-' + this.dataName,true) :
8823             this.el.dom; 
8824         
8825         if(!node || node.parentNode == el){
8826                     return node;
8827             }
8828             var p = node.parentNode;
8829             while(p && p != el){
8830             if(p.parentNode == el){
8831                 return p;
8832             }
8833             p = p.parentNode;
8834         }
8835             return null;
8836     },
8837
8838     /** @ignore */
8839     onClick : function(e){
8840         var item = this.findItemFromChild(e.getTarget());
8841         if(item){
8842             var index = this.indexOf(item);
8843             if(this.onItemClick(item, index, e) !== false){
8844                 this.fireEvent("click", this, index, item, e);
8845             }
8846         }else{
8847             this.clearSelections();
8848         }
8849     },
8850
8851     /** @ignore */
8852     onContextMenu : function(e){
8853         var item = this.findItemFromChild(e.getTarget());
8854         if(item){
8855             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8856         }
8857     },
8858
8859     /** @ignore */
8860     onDblClick : function(e){
8861         var item = this.findItemFromChild(e.getTarget());
8862         if(item){
8863             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8864         }
8865     },
8866
8867     onItemClick : function(item, index, e)
8868     {
8869         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8870             return false;
8871         }
8872         if (this.toggleSelect) {
8873             var m = this.isSelected(item) ? 'unselect' : 'select';
8874             Roo.log(m);
8875             var _t = this;
8876             _t[m](item, true, false);
8877             return true;
8878         }
8879         if(this.multiSelect || this.singleSelect){
8880             if(this.multiSelect && e.shiftKey && this.lastSelection){
8881                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8882             }else{
8883                 this.select(item, this.multiSelect && e.ctrlKey);
8884                 this.lastSelection = item;
8885             }
8886             
8887             if(!this.tickable){
8888                 e.preventDefault();
8889             }
8890             
8891         }
8892         return true;
8893     },
8894
8895     /**
8896      * Get the number of selected nodes.
8897      * @return {Number}
8898      */
8899     getSelectionCount : function(){
8900         return this.selections.length;
8901     },
8902
8903     /**
8904      * Get the currently selected nodes.
8905      * @return {Array} An array of HTMLElements
8906      */
8907     getSelectedNodes : function(){
8908         return this.selections;
8909     },
8910
8911     /**
8912      * Get the indexes of the selected nodes.
8913      * @return {Array}
8914      */
8915     getSelectedIndexes : function(){
8916         var indexes = [], s = this.selections;
8917         for(var i = 0, len = s.length; i < len; i++){
8918             indexes.push(s[i].nodeIndex);
8919         }
8920         return indexes;
8921     },
8922
8923     /**
8924      * Clear all selections
8925      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8926      */
8927     clearSelections : function(suppressEvent){
8928         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8929             this.cmp.elements = this.selections;
8930             this.cmp.removeClass(this.selectedClass);
8931             this.selections = [];
8932             if(!suppressEvent){
8933                 this.fireEvent("selectionchange", this, this.selections);
8934             }
8935         }
8936     },
8937
8938     /**
8939      * Returns true if the passed node is selected
8940      * @param {HTMLElement/Number} node The node or node index
8941      * @return {Boolean}
8942      */
8943     isSelected : function(node){
8944         var s = this.selections;
8945         if(s.length < 1){
8946             return false;
8947         }
8948         node = this.getNode(node);
8949         return s.indexOf(node) !== -1;
8950     },
8951
8952     /**
8953      * Selects nodes.
8954      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
8955      * @param {Boolean} keepExisting (optional) true to keep existing selections
8956      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8957      */
8958     select : function(nodeInfo, keepExisting, suppressEvent){
8959         if(nodeInfo instanceof Array){
8960             if(!keepExisting){
8961                 this.clearSelections(true);
8962             }
8963             for(var i = 0, len = nodeInfo.length; i < len; i++){
8964                 this.select(nodeInfo[i], true, true);
8965             }
8966             return;
8967         } 
8968         var node = this.getNode(nodeInfo);
8969         if(!node || this.isSelected(node)){
8970             return; // already selected.
8971         }
8972         if(!keepExisting){
8973             this.clearSelections(true);
8974         }
8975         
8976         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8977             Roo.fly(node).addClass(this.selectedClass);
8978             this.selections.push(node);
8979             if(!suppressEvent){
8980                 this.fireEvent("selectionchange", this, this.selections);
8981             }
8982         }
8983         
8984         
8985     },
8986       /**
8987      * Unselects nodes.
8988      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
8989      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8990      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8991      */
8992     unselect : function(nodeInfo, keepExisting, suppressEvent)
8993     {
8994         if(nodeInfo instanceof Array){
8995             Roo.each(this.selections, function(s) {
8996                 this.unselect(s, nodeInfo);
8997             }, this);
8998             return;
8999         }
9000         var node = this.getNode(nodeInfo);
9001         if(!node || !this.isSelected(node)){
9002             Roo.log("not selected");
9003             return; // not selected.
9004         }
9005         // fireevent???
9006         var ns = [];
9007         Roo.each(this.selections, function(s) {
9008             if (s == node ) {
9009                 Roo.fly(node).removeClass(this.selectedClass);
9010
9011                 return;
9012             }
9013             ns.push(s);
9014         },this);
9015         
9016         this.selections= ns;
9017         this.fireEvent("selectionchange", this, this.selections);
9018     },
9019
9020     /**
9021      * Gets a template node.
9022      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9023      * @return {HTMLElement} The node or null if it wasn't found
9024      */
9025     getNode : function(nodeInfo){
9026         if(typeof nodeInfo == "string"){
9027             return document.getElementById(nodeInfo);
9028         }else if(typeof nodeInfo == "number"){
9029             return this.nodes[nodeInfo];
9030         }
9031         return nodeInfo;
9032     },
9033
9034     /**
9035      * Gets a range template nodes.
9036      * @param {Number} startIndex
9037      * @param {Number} endIndex
9038      * @return {Array} An array of nodes
9039      */
9040     getNodes : function(start, end){
9041         var ns = this.nodes;
9042         start = start || 0;
9043         end = typeof end == "undefined" ? ns.length - 1 : end;
9044         var nodes = [];
9045         if(start <= end){
9046             for(var i = start; i <= end; i++){
9047                 nodes.push(ns[i]);
9048             }
9049         } else{
9050             for(var i = start; i >= end; i--){
9051                 nodes.push(ns[i]);
9052             }
9053         }
9054         return nodes;
9055     },
9056
9057     /**
9058      * Finds the index of the passed node
9059      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9060      * @return {Number} The index of the node or -1
9061      */
9062     indexOf : function(node){
9063         node = this.getNode(node);
9064         if(typeof node.nodeIndex == "number"){
9065             return node.nodeIndex;
9066         }
9067         var ns = this.nodes;
9068         for(var i = 0, len = ns.length; i < len; i++){
9069             if(ns[i] == node){
9070                 return i;
9071             }
9072         }
9073         return -1;
9074     }
9075 });
9076 /*
9077  * Based on:
9078  * Ext JS Library 1.1.1
9079  * Copyright(c) 2006-2007, Ext JS, LLC.
9080  *
9081  * Originally Released Under LGPL - original licence link has changed is not relivant.
9082  *
9083  * Fork - LGPL
9084  * <script type="text/javascript">
9085  */
9086
9087 /**
9088  * @class Roo.JsonView
9089  * @extends Roo.View
9090  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9091 <pre><code>
9092 var view = new Roo.JsonView({
9093     container: "my-element",
9094     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9095     multiSelect: true, 
9096     jsonRoot: "data" 
9097 });
9098
9099 // listen for node click?
9100 view.on("click", function(vw, index, node, e){
9101     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9102 });
9103
9104 // direct load of JSON data
9105 view.load("foobar.php");
9106
9107 // Example from my blog list
9108 var tpl = new Roo.Template(
9109     '&lt;div class="entry"&gt;' +
9110     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9111     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9112     "&lt;/div&gt;&lt;hr /&gt;"
9113 );
9114
9115 var moreView = new Roo.JsonView({
9116     container :  "entry-list", 
9117     template : tpl,
9118     jsonRoot: "posts"
9119 });
9120 moreView.on("beforerender", this.sortEntries, this);
9121 moreView.load({
9122     url: "/blog/get-posts.php",
9123     params: "allposts=true",
9124     text: "Loading Blog Entries..."
9125 });
9126 </code></pre>
9127
9128 * Note: old code is supported with arguments : (container, template, config)
9129
9130
9131  * @constructor
9132  * Create a new JsonView
9133  * 
9134  * @param {Object} config The config object
9135  * 
9136  */
9137 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9138     
9139     
9140     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9141
9142     var um = this.el.getUpdateManager();
9143     um.setRenderer(this);
9144     um.on("update", this.onLoad, this);
9145     um.on("failure", this.onLoadException, this);
9146
9147     /**
9148      * @event beforerender
9149      * Fires before rendering of the downloaded JSON data.
9150      * @param {Roo.JsonView} this
9151      * @param {Object} data The JSON data loaded
9152      */
9153     /**
9154      * @event load
9155      * Fires when data is loaded.
9156      * @param {Roo.JsonView} this
9157      * @param {Object} data The JSON data loaded
9158      * @param {Object} response The raw Connect response object
9159      */
9160     /**
9161      * @event loadexception
9162      * Fires when loading fails.
9163      * @param {Roo.JsonView} this
9164      * @param {Object} response The raw Connect response object
9165      */
9166     this.addEvents({
9167         'beforerender' : true,
9168         'load' : true,
9169         'loadexception' : true
9170     });
9171 };
9172 Roo.extend(Roo.JsonView, Roo.View, {
9173     /**
9174      * @type {String} The root property in the loaded JSON object that contains the data
9175      */
9176     jsonRoot : "",
9177
9178     /**
9179      * Refreshes the view.
9180      */
9181     refresh : function(){
9182         this.clearSelections();
9183         this.el.update("");
9184         var html = [];
9185         var o = this.jsonData;
9186         if(o && o.length > 0){
9187             for(var i = 0, len = o.length; i < len; i++){
9188                 var data = this.prepareData(o[i], i, o);
9189                 html[html.length] = this.tpl.apply(data);
9190             }
9191         }else{
9192             html.push(this.emptyText);
9193         }
9194         this.el.update(html.join(""));
9195         this.nodes = this.el.dom.childNodes;
9196         this.updateIndexes(0);
9197     },
9198
9199     /**
9200      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9201      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9202      <pre><code>
9203      view.load({
9204          url: "your-url.php",
9205          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9206          callback: yourFunction,
9207          scope: yourObject, //(optional scope)
9208          discardUrl: false,
9209          nocache: false,
9210          text: "Loading...",
9211          timeout: 30,
9212          scripts: false
9213      });
9214      </code></pre>
9215      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9216      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9217      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9218      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9219      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9220      */
9221     load : function(){
9222         var um = this.el.getUpdateManager();
9223         um.update.apply(um, arguments);
9224     },
9225
9226     render : function(el, response){
9227         this.clearSelections();
9228         this.el.update("");
9229         var o;
9230         try{
9231             o = Roo.util.JSON.decode(response.responseText);
9232             if(this.jsonRoot){
9233                 
9234                 o = o[this.jsonRoot];
9235             }
9236         } catch(e){
9237         }
9238         /**
9239          * The current JSON data or null
9240          */
9241         this.jsonData = o;
9242         this.beforeRender();
9243         this.refresh();
9244     },
9245
9246 /**
9247  * Get the number of records in the current JSON dataset
9248  * @return {Number}
9249  */
9250     getCount : function(){
9251         return this.jsonData ? this.jsonData.length : 0;
9252     },
9253
9254 /**
9255  * Returns the JSON object for the specified node(s)
9256  * @param {HTMLElement/Array} node The node or an array of nodes
9257  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9258  * you get the JSON object for the node
9259  */
9260     getNodeData : function(node){
9261         if(node instanceof Array){
9262             var data = [];
9263             for(var i = 0, len = node.length; i < len; i++){
9264                 data.push(this.getNodeData(node[i]));
9265             }
9266             return data;
9267         }
9268         return this.jsonData[this.indexOf(node)] || null;
9269     },
9270
9271     beforeRender : function(){
9272         this.snapshot = this.jsonData;
9273         if(this.sortInfo){
9274             this.sort.apply(this, this.sortInfo);
9275         }
9276         this.fireEvent("beforerender", this, this.jsonData);
9277     },
9278
9279     onLoad : function(el, o){
9280         this.fireEvent("load", this, this.jsonData, o);
9281     },
9282
9283     onLoadException : function(el, o){
9284         this.fireEvent("loadexception", this, o);
9285     },
9286
9287 /**
9288  * Filter the data by a specific property.
9289  * @param {String} property A property on your JSON objects
9290  * @param {String/RegExp} value Either string that the property values
9291  * should start with, or a RegExp to test against the property
9292  */
9293     filter : function(property, value){
9294         if(this.jsonData){
9295             var data = [];
9296             var ss = this.snapshot;
9297             if(typeof value == "string"){
9298                 var vlen = value.length;
9299                 if(vlen == 0){
9300                     this.clearFilter();
9301                     return;
9302                 }
9303                 value = value.toLowerCase();
9304                 for(var i = 0, len = ss.length; i < len; i++){
9305                     var o = ss[i];
9306                     if(o[property].substr(0, vlen).toLowerCase() == value){
9307                         data.push(o);
9308                     }
9309                 }
9310             } else if(value.exec){ // regex?
9311                 for(var i = 0, len = ss.length; i < len; i++){
9312                     var o = ss[i];
9313                     if(value.test(o[property])){
9314                         data.push(o);
9315                     }
9316                 }
9317             } else{
9318                 return;
9319             }
9320             this.jsonData = data;
9321             this.refresh();
9322         }
9323     },
9324
9325 /**
9326  * Filter by a function. The passed function will be called with each
9327  * object in the current dataset. If the function returns true the value is kept,
9328  * otherwise it is filtered.
9329  * @param {Function} fn
9330  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9331  */
9332     filterBy : function(fn, scope){
9333         if(this.jsonData){
9334             var data = [];
9335             var ss = this.snapshot;
9336             for(var i = 0, len = ss.length; i < len; i++){
9337                 var o = ss[i];
9338                 if(fn.call(scope || this, o)){
9339                     data.push(o);
9340                 }
9341             }
9342             this.jsonData = data;
9343             this.refresh();
9344         }
9345     },
9346
9347 /**
9348  * Clears the current filter.
9349  */
9350     clearFilter : function(){
9351         if(this.snapshot && this.jsonData != this.snapshot){
9352             this.jsonData = this.snapshot;
9353             this.refresh();
9354         }
9355     },
9356
9357
9358 /**
9359  * Sorts the data for this view and refreshes it.
9360  * @param {String} property A property on your JSON objects to sort on
9361  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9362  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9363  */
9364     sort : function(property, dir, sortType){
9365         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9366         if(this.jsonData){
9367             var p = property;
9368             var dsc = dir && dir.toLowerCase() == "desc";
9369             var f = function(o1, o2){
9370                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9371                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9372                 ;
9373                 if(v1 < v2){
9374                     return dsc ? +1 : -1;
9375                 } else if(v1 > v2){
9376                     return dsc ? -1 : +1;
9377                 } else{
9378                     return 0;
9379                 }
9380             };
9381             this.jsonData.sort(f);
9382             this.refresh();
9383             if(this.jsonData != this.snapshot){
9384                 this.snapshot.sort(f);
9385             }
9386         }
9387     }
9388 });/*
9389  * Based on:
9390  * Ext JS Library 1.1.1
9391  * Copyright(c) 2006-2007, Ext JS, LLC.
9392  *
9393  * Originally Released Under LGPL - original licence link has changed is not relivant.
9394  *
9395  * Fork - LGPL
9396  * <script type="text/javascript">
9397  */
9398  
9399
9400 /**
9401  * @class Roo.ColorPalette
9402  * @extends Roo.Component
9403  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9404  * Here's an example of typical usage:
9405  * <pre><code>
9406 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9407 cp.render('my-div');
9408
9409 cp.on('select', function(palette, selColor){
9410     // do something with selColor
9411 });
9412 </code></pre>
9413  * @constructor
9414  * Create a new ColorPalette
9415  * @param {Object} config The config object
9416  */
9417 Roo.ColorPalette = function(config){
9418     Roo.ColorPalette.superclass.constructor.call(this, config);
9419     this.addEvents({
9420         /**
9421              * @event select
9422              * Fires when a color is selected
9423              * @param {ColorPalette} this
9424              * @param {String} color The 6-digit color hex code (without the # symbol)
9425              */
9426         select: true
9427     });
9428
9429     if(this.handler){
9430         this.on("select", this.handler, this.scope, true);
9431     }
9432 };
9433 Roo.extend(Roo.ColorPalette, Roo.Component, {
9434     /**
9435      * @cfg {String} itemCls
9436      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9437      */
9438     itemCls : "x-color-palette",
9439     /**
9440      * @cfg {String} value
9441      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9442      * the hex codes are case-sensitive.
9443      */
9444     value : null,
9445     clickEvent:'click',
9446     // private
9447     ctype: "Roo.ColorPalette",
9448
9449     /**
9450      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9451      */
9452     allowReselect : false,
9453
9454     /**
9455      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9456      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9457      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9458      * of colors with the width setting until the box is symmetrical.</p>
9459      * <p>You can override individual colors if needed:</p>
9460      * <pre><code>
9461 var cp = new Roo.ColorPalette();
9462 cp.colors[0] = "FF0000";  // change the first box to red
9463 </code></pre>
9464
9465 Or you can provide a custom array of your own for complete control:
9466 <pre><code>
9467 var cp = new Roo.ColorPalette();
9468 cp.colors = ["000000", "993300", "333300"];
9469 </code></pre>
9470      * @type Array
9471      */
9472     colors : [
9473         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9474         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9475         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9476         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9477         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9478     ],
9479
9480     // private
9481     onRender : function(container, position){
9482         var t = new Roo.MasterTemplate(
9483             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9484         );
9485         var c = this.colors;
9486         for(var i = 0, len = c.length; i < len; i++){
9487             t.add([c[i]]);
9488         }
9489         var el = document.createElement("div");
9490         el.className = this.itemCls;
9491         t.overwrite(el);
9492         container.dom.insertBefore(el, position);
9493         this.el = Roo.get(el);
9494         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9495         if(this.clickEvent != 'click'){
9496             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9497         }
9498     },
9499
9500     // private
9501     afterRender : function(){
9502         Roo.ColorPalette.superclass.afterRender.call(this);
9503         if(this.value){
9504             var s = this.value;
9505             this.value = null;
9506             this.select(s);
9507         }
9508     },
9509
9510     // private
9511     handleClick : function(e, t){
9512         e.preventDefault();
9513         if(!this.disabled){
9514             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9515             this.select(c.toUpperCase());
9516         }
9517     },
9518
9519     /**
9520      * Selects the specified color in the palette (fires the select event)
9521      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9522      */
9523     select : function(color){
9524         color = color.replace("#", "");
9525         if(color != this.value || this.allowReselect){
9526             var el = this.el;
9527             if(this.value){
9528                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9529             }
9530             el.child("a.color-"+color).addClass("x-color-palette-sel");
9531             this.value = color;
9532             this.fireEvent("select", this, color);
9533         }
9534     }
9535 });/*
9536  * Based on:
9537  * Ext JS Library 1.1.1
9538  * Copyright(c) 2006-2007, Ext JS, LLC.
9539  *
9540  * Originally Released Under LGPL - original licence link has changed is not relivant.
9541  *
9542  * Fork - LGPL
9543  * <script type="text/javascript">
9544  */
9545  
9546 /**
9547  * @class Roo.DatePicker
9548  * @extends Roo.Component
9549  * Simple date picker class.
9550  * @constructor
9551  * Create a new DatePicker
9552  * @param {Object} config The config object
9553  */
9554 Roo.DatePicker = function(config){
9555     Roo.DatePicker.superclass.constructor.call(this, config);
9556
9557     this.value = config && config.value ?
9558                  config.value.clearTime() : new Date().clearTime();
9559
9560     this.addEvents({
9561         /**
9562              * @event select
9563              * Fires when a date is selected
9564              * @param {DatePicker} this
9565              * @param {Date} date The selected date
9566              */
9567         'select': true,
9568         /**
9569              * @event monthchange
9570              * Fires when the displayed month changes 
9571              * @param {DatePicker} this
9572              * @param {Date} date The selected month
9573              */
9574         'monthchange': true
9575     });
9576
9577     if(this.handler){
9578         this.on("select", this.handler,  this.scope || this);
9579     }
9580     // build the disabledDatesRE
9581     if(!this.disabledDatesRE && this.disabledDates){
9582         var dd = this.disabledDates;
9583         var re = "(?:";
9584         for(var i = 0; i < dd.length; i++){
9585             re += dd[i];
9586             if(i != dd.length-1) re += "|";
9587         }
9588         this.disabledDatesRE = new RegExp(re + ")");
9589     }
9590 };
9591
9592 Roo.extend(Roo.DatePicker, Roo.Component, {
9593     /**
9594      * @cfg {String} todayText
9595      * The text to display on the button that selects the current date (defaults to "Today")
9596      */
9597     todayText : "Today",
9598     /**
9599      * @cfg {String} okText
9600      * The text to display on the ok button
9601      */
9602     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9603     /**
9604      * @cfg {String} cancelText
9605      * The text to display on the cancel button
9606      */
9607     cancelText : "Cancel",
9608     /**
9609      * @cfg {String} todayTip
9610      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9611      */
9612     todayTip : "{0} (Spacebar)",
9613     /**
9614      * @cfg {Date} minDate
9615      * Minimum allowable date (JavaScript date object, defaults to null)
9616      */
9617     minDate : null,
9618     /**
9619      * @cfg {Date} maxDate
9620      * Maximum allowable date (JavaScript date object, defaults to null)
9621      */
9622     maxDate : null,
9623     /**
9624      * @cfg {String} minText
9625      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9626      */
9627     minText : "This date is before the minimum date",
9628     /**
9629      * @cfg {String} maxText
9630      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9631      */
9632     maxText : "This date is after the maximum date",
9633     /**
9634      * @cfg {String} format
9635      * The default date format string which can be overriden for localization support.  The format must be
9636      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9637      */
9638     format : "m/d/y",
9639     /**
9640      * @cfg {Array} disabledDays
9641      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9642      */
9643     disabledDays : null,
9644     /**
9645      * @cfg {String} disabledDaysText
9646      * The tooltip to display when the date falls on a disabled day (defaults to "")
9647      */
9648     disabledDaysText : "",
9649     /**
9650      * @cfg {RegExp} disabledDatesRE
9651      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9652      */
9653     disabledDatesRE : null,
9654     /**
9655      * @cfg {String} disabledDatesText
9656      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9657      */
9658     disabledDatesText : "",
9659     /**
9660      * @cfg {Boolean} constrainToViewport
9661      * True to constrain the date picker to the viewport (defaults to true)
9662      */
9663     constrainToViewport : true,
9664     /**
9665      * @cfg {Array} monthNames
9666      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9667      */
9668     monthNames : Date.monthNames,
9669     /**
9670      * @cfg {Array} dayNames
9671      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9672      */
9673     dayNames : Date.dayNames,
9674     /**
9675      * @cfg {String} nextText
9676      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9677      */
9678     nextText: 'Next Month (Control+Right)',
9679     /**
9680      * @cfg {String} prevText
9681      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9682      */
9683     prevText: 'Previous Month (Control+Left)',
9684     /**
9685      * @cfg {String} monthYearText
9686      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9687      */
9688     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9689     /**
9690      * @cfg {Number} startDay
9691      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9692      */
9693     startDay : 0,
9694     /**
9695      * @cfg {Bool} showClear
9696      * Show a clear button (usefull for date form elements that can be blank.)
9697      */
9698     
9699     showClear: false,
9700     
9701     /**
9702      * Sets the value of the date field
9703      * @param {Date} value The date to set
9704      */
9705     setValue : function(value){
9706         var old = this.value;
9707         
9708         if (typeof(value) == 'string') {
9709          
9710             value = Date.parseDate(value, this.format);
9711         }
9712         if (!value) {
9713             value = new Date();
9714         }
9715         
9716         this.value = value.clearTime(true);
9717         if(this.el){
9718             this.update(this.value);
9719         }
9720     },
9721
9722     /**
9723      * Gets the current selected value of the date field
9724      * @return {Date} The selected date
9725      */
9726     getValue : function(){
9727         return this.value;
9728     },
9729
9730     // private
9731     focus : function(){
9732         if(this.el){
9733             this.update(this.activeDate);
9734         }
9735     },
9736
9737     // privateval
9738     onRender : function(container, position){
9739         
9740         var m = [
9741              '<table cellspacing="0">',
9742                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
9743                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9744         var dn = this.dayNames;
9745         for(var i = 0; i < 7; i++){
9746             var d = this.startDay+i;
9747             if(d > 6){
9748                 d = d-7;
9749             }
9750             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9751         }
9752         m[m.length] = "</tr></thead><tbody><tr>";
9753         for(var i = 0; i < 42; i++) {
9754             if(i % 7 == 0 && i != 0){
9755                 m[m.length] = "</tr><tr>";
9756             }
9757             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9758         }
9759         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9760             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9761
9762         var el = document.createElement("div");
9763         el.className = "x-date-picker";
9764         el.innerHTML = m.join("");
9765
9766         container.dom.insertBefore(el, position);
9767
9768         this.el = Roo.get(el);
9769         this.eventEl = Roo.get(el.firstChild);
9770
9771         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9772             handler: this.showPrevMonth,
9773             scope: this,
9774             preventDefault:true,
9775             stopDefault:true
9776         });
9777
9778         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9779             handler: this.showNextMonth,
9780             scope: this,
9781             preventDefault:true,
9782             stopDefault:true
9783         });
9784
9785         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9786
9787         this.monthPicker = this.el.down('div.x-date-mp');
9788         this.monthPicker.enableDisplayMode('block');
9789         
9790         var kn = new Roo.KeyNav(this.eventEl, {
9791             "left" : function(e){
9792                 e.ctrlKey ?
9793                     this.showPrevMonth() :
9794                     this.update(this.activeDate.add("d", -1));
9795             },
9796
9797             "right" : function(e){
9798                 e.ctrlKey ?
9799                     this.showNextMonth() :
9800                     this.update(this.activeDate.add("d", 1));
9801             },
9802
9803             "up" : function(e){
9804                 e.ctrlKey ?
9805                     this.showNextYear() :
9806                     this.update(this.activeDate.add("d", -7));
9807             },
9808
9809             "down" : function(e){
9810                 e.ctrlKey ?
9811                     this.showPrevYear() :
9812                     this.update(this.activeDate.add("d", 7));
9813             },
9814
9815             "pageUp" : function(e){
9816                 this.showNextMonth();
9817             },
9818
9819             "pageDown" : function(e){
9820                 this.showPrevMonth();
9821             },
9822
9823             "enter" : function(e){
9824                 e.stopPropagation();
9825                 return true;
9826             },
9827
9828             scope : this
9829         });
9830
9831         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9832
9833         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9834
9835         this.el.unselectable();
9836         
9837         this.cells = this.el.select("table.x-date-inner tbody td");
9838         this.textNodes = this.el.query("table.x-date-inner tbody span");
9839
9840         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9841             text: "&#160;",
9842             tooltip: this.monthYearText
9843         });
9844
9845         this.mbtn.on('click', this.showMonthPicker, this);
9846         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9847
9848
9849         var today = (new Date()).dateFormat(this.format);
9850         
9851         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9852         if (this.showClear) {
9853             baseTb.add( new Roo.Toolbar.Fill());
9854         }
9855         baseTb.add({
9856             text: String.format(this.todayText, today),
9857             tooltip: String.format(this.todayTip, today),
9858             handler: this.selectToday,
9859             scope: this
9860         });
9861         
9862         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9863             
9864         //});
9865         if (this.showClear) {
9866             
9867             baseTb.add( new Roo.Toolbar.Fill());
9868             baseTb.add({
9869                 text: '&#160;',
9870                 cls: 'x-btn-icon x-btn-clear',
9871                 handler: function() {
9872                     //this.value = '';
9873                     this.fireEvent("select", this, '');
9874                 },
9875                 scope: this
9876             });
9877         }
9878         
9879         
9880         if(Roo.isIE){
9881             this.el.repaint();
9882         }
9883         this.update(this.value);
9884     },
9885
9886     createMonthPicker : function(){
9887         if(!this.monthPicker.dom.firstChild){
9888             var buf = ['<table border="0" cellspacing="0">'];
9889             for(var i = 0; i < 6; i++){
9890                 buf.push(
9891                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9892                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9893                     i == 0 ?
9894                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
9895                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9896                 );
9897             }
9898             buf.push(
9899                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9900                     this.okText,
9901                     '</button><button type="button" class="x-date-mp-cancel">',
9902                     this.cancelText,
9903                     '</button></td></tr>',
9904                 '</table>'
9905             );
9906             this.monthPicker.update(buf.join(''));
9907             this.monthPicker.on('click', this.onMonthClick, this);
9908             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9909
9910             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9911             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9912
9913             this.mpMonths.each(function(m, a, i){
9914                 i += 1;
9915                 if((i%2) == 0){
9916                     m.dom.xmonth = 5 + Math.round(i * .5);
9917                 }else{
9918                     m.dom.xmonth = Math.round((i-1) * .5);
9919                 }
9920             });
9921         }
9922     },
9923
9924     showMonthPicker : function(){
9925         this.createMonthPicker();
9926         var size = this.el.getSize();
9927         this.monthPicker.setSize(size);
9928         this.monthPicker.child('table').setSize(size);
9929
9930         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9931         this.updateMPMonth(this.mpSelMonth);
9932         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9933         this.updateMPYear(this.mpSelYear);
9934
9935         this.monthPicker.slideIn('t', {duration:.2});
9936     },
9937
9938     updateMPYear : function(y){
9939         this.mpyear = y;
9940         var ys = this.mpYears.elements;
9941         for(var i = 1; i <= 10; i++){
9942             var td = ys[i-1], y2;
9943             if((i%2) == 0){
9944                 y2 = y + Math.round(i * .5);
9945                 td.firstChild.innerHTML = y2;
9946                 td.xyear = y2;
9947             }else{
9948                 y2 = y - (5-Math.round(i * .5));
9949                 td.firstChild.innerHTML = y2;
9950                 td.xyear = y2;
9951             }
9952             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9953         }
9954     },
9955
9956     updateMPMonth : function(sm){
9957         this.mpMonths.each(function(m, a, i){
9958             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9959         });
9960     },
9961
9962     selectMPMonth: function(m){
9963         
9964     },
9965
9966     onMonthClick : function(e, t){
9967         e.stopEvent();
9968         var el = new Roo.Element(t), pn;
9969         if(el.is('button.x-date-mp-cancel')){
9970             this.hideMonthPicker();
9971         }
9972         else if(el.is('button.x-date-mp-ok')){
9973             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9974             this.hideMonthPicker();
9975         }
9976         else if(pn = el.up('td.x-date-mp-month', 2)){
9977             this.mpMonths.removeClass('x-date-mp-sel');
9978             pn.addClass('x-date-mp-sel');
9979             this.mpSelMonth = pn.dom.xmonth;
9980         }
9981         else if(pn = el.up('td.x-date-mp-year', 2)){
9982             this.mpYears.removeClass('x-date-mp-sel');
9983             pn.addClass('x-date-mp-sel');
9984             this.mpSelYear = pn.dom.xyear;
9985         }
9986         else if(el.is('a.x-date-mp-prev')){
9987             this.updateMPYear(this.mpyear-10);
9988         }
9989         else if(el.is('a.x-date-mp-next')){
9990             this.updateMPYear(this.mpyear+10);
9991         }
9992     },
9993
9994     onMonthDblClick : function(e, t){
9995         e.stopEvent();
9996         var el = new Roo.Element(t), pn;
9997         if(pn = el.up('td.x-date-mp-month', 2)){
9998             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
9999             this.hideMonthPicker();
10000         }
10001         else if(pn = el.up('td.x-date-mp-year', 2)){
10002             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10003             this.hideMonthPicker();
10004         }
10005     },
10006
10007     hideMonthPicker : function(disableAnim){
10008         if(this.monthPicker){
10009             if(disableAnim === true){
10010                 this.monthPicker.hide();
10011             }else{
10012                 this.monthPicker.slideOut('t', {duration:.2});
10013             }
10014         }
10015     },
10016
10017     // private
10018     showPrevMonth : function(e){
10019         this.update(this.activeDate.add("mo", -1));
10020     },
10021
10022     // private
10023     showNextMonth : function(e){
10024         this.update(this.activeDate.add("mo", 1));
10025     },
10026
10027     // private
10028     showPrevYear : function(){
10029         this.update(this.activeDate.add("y", -1));
10030     },
10031
10032     // private
10033     showNextYear : function(){
10034         this.update(this.activeDate.add("y", 1));
10035     },
10036
10037     // private
10038     handleMouseWheel : function(e){
10039         var delta = e.getWheelDelta();
10040         if(delta > 0){
10041             this.showPrevMonth();
10042             e.stopEvent();
10043         } else if(delta < 0){
10044             this.showNextMonth();
10045             e.stopEvent();
10046         }
10047     },
10048
10049     // private
10050     handleDateClick : function(e, t){
10051         e.stopEvent();
10052         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10053             this.setValue(new Date(t.dateValue));
10054             this.fireEvent("select", this, this.value);
10055         }
10056     },
10057
10058     // private
10059     selectToday : function(){
10060         this.setValue(new Date().clearTime());
10061         this.fireEvent("select", this, this.value);
10062     },
10063
10064     // private
10065     update : function(date)
10066     {
10067         var vd = this.activeDate;
10068         this.activeDate = date;
10069         if(vd && this.el){
10070             var t = date.getTime();
10071             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10072                 this.cells.removeClass("x-date-selected");
10073                 this.cells.each(function(c){
10074                    if(c.dom.firstChild.dateValue == t){
10075                        c.addClass("x-date-selected");
10076                        setTimeout(function(){
10077                             try{c.dom.firstChild.focus();}catch(e){}
10078                        }, 50);
10079                        return false;
10080                    }
10081                 });
10082                 return;
10083             }
10084         }
10085         
10086         var days = date.getDaysInMonth();
10087         var firstOfMonth = date.getFirstDateOfMonth();
10088         var startingPos = firstOfMonth.getDay()-this.startDay;
10089
10090         if(startingPos <= this.startDay){
10091             startingPos += 7;
10092         }
10093
10094         var pm = date.add("mo", -1);
10095         var prevStart = pm.getDaysInMonth()-startingPos;
10096
10097         var cells = this.cells.elements;
10098         var textEls = this.textNodes;
10099         days += startingPos;
10100
10101         // convert everything to numbers so it's fast
10102         var day = 86400000;
10103         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10104         var today = new Date().clearTime().getTime();
10105         var sel = date.clearTime().getTime();
10106         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10107         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10108         var ddMatch = this.disabledDatesRE;
10109         var ddText = this.disabledDatesText;
10110         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10111         var ddaysText = this.disabledDaysText;
10112         var format = this.format;
10113
10114         var setCellClass = function(cal, cell){
10115             cell.title = "";
10116             var t = d.getTime();
10117             cell.firstChild.dateValue = t;
10118             if(t == today){
10119                 cell.className += " x-date-today";
10120                 cell.title = cal.todayText;
10121             }
10122             if(t == sel){
10123                 cell.className += " x-date-selected";
10124                 setTimeout(function(){
10125                     try{cell.firstChild.focus();}catch(e){}
10126                 }, 50);
10127             }
10128             // disabling
10129             if(t < min) {
10130                 cell.className = " x-date-disabled";
10131                 cell.title = cal.minText;
10132                 return;
10133             }
10134             if(t > max) {
10135                 cell.className = " x-date-disabled";
10136                 cell.title = cal.maxText;
10137                 return;
10138             }
10139             if(ddays){
10140                 if(ddays.indexOf(d.getDay()) != -1){
10141                     cell.title = ddaysText;
10142                     cell.className = " x-date-disabled";
10143                 }
10144             }
10145             if(ddMatch && format){
10146                 var fvalue = d.dateFormat(format);
10147                 if(ddMatch.test(fvalue)){
10148                     cell.title = ddText.replace("%0", fvalue);
10149                     cell.className = " x-date-disabled";
10150                 }
10151             }
10152         };
10153
10154         var i = 0;
10155         for(; i < startingPos; i++) {
10156             textEls[i].innerHTML = (++prevStart);
10157             d.setDate(d.getDate()+1);
10158             cells[i].className = "x-date-prevday";
10159             setCellClass(this, cells[i]);
10160         }
10161         for(; i < days; i++){
10162             intDay = i - startingPos + 1;
10163             textEls[i].innerHTML = (intDay);
10164             d.setDate(d.getDate()+1);
10165             cells[i].className = "x-date-active";
10166             setCellClass(this, cells[i]);
10167         }
10168         var extraDays = 0;
10169         for(; i < 42; i++) {
10170              textEls[i].innerHTML = (++extraDays);
10171              d.setDate(d.getDate()+1);
10172              cells[i].className = "x-date-nextday";
10173              setCellClass(this, cells[i]);
10174         }
10175
10176         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10177         this.fireEvent('monthchange', this, date);
10178         
10179         if(!this.internalRender){
10180             var main = this.el.dom.firstChild;
10181             var w = main.offsetWidth;
10182             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10183             Roo.fly(main).setWidth(w);
10184             this.internalRender = true;
10185             // opera does not respect the auto grow header center column
10186             // then, after it gets a width opera refuses to recalculate
10187             // without a second pass
10188             if(Roo.isOpera && !this.secondPass){
10189                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10190                 this.secondPass = true;
10191                 this.update.defer(10, this, [date]);
10192             }
10193         }
10194         
10195         
10196     }
10197 });        /*
10198  * Based on:
10199  * Ext JS Library 1.1.1
10200  * Copyright(c) 2006-2007, Ext JS, LLC.
10201  *
10202  * Originally Released Under LGPL - original licence link has changed is not relivant.
10203  *
10204  * Fork - LGPL
10205  * <script type="text/javascript">
10206  */
10207 /**
10208  * @class Roo.TabPanel
10209  * @extends Roo.util.Observable
10210  * A lightweight tab container.
10211  * <br><br>
10212  * Usage:
10213  * <pre><code>
10214 // basic tabs 1, built from existing content
10215 var tabs = new Roo.TabPanel("tabs1");
10216 tabs.addTab("script", "View Script");
10217 tabs.addTab("markup", "View Markup");
10218 tabs.activate("script");
10219
10220 // more advanced tabs, built from javascript
10221 var jtabs = new Roo.TabPanel("jtabs");
10222 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10223
10224 // set up the UpdateManager
10225 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10226 var updater = tab2.getUpdateManager();
10227 updater.setDefaultUrl("ajax1.htm");
10228 tab2.on('activate', updater.refresh, updater, true);
10229
10230 // Use setUrl for Ajax loading
10231 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10232 tab3.setUrl("ajax2.htm", null, true);
10233
10234 // Disabled tab
10235 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10236 tab4.disable();
10237
10238 jtabs.activate("jtabs-1");
10239  * </code></pre>
10240  * @constructor
10241  * Create a new TabPanel.
10242  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10243  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10244  */
10245 Roo.TabPanel = function(container, config){
10246     /**
10247     * The container element for this TabPanel.
10248     * @type Roo.Element
10249     */
10250     this.el = Roo.get(container, true);
10251     if(config){
10252         if(typeof config == "boolean"){
10253             this.tabPosition = config ? "bottom" : "top";
10254         }else{
10255             Roo.apply(this, config);
10256         }
10257     }
10258     if(this.tabPosition == "bottom"){
10259         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10260         this.el.addClass("x-tabs-bottom");
10261     }
10262     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10263     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10264     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10265     if(Roo.isIE){
10266         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10267     }
10268     if(this.tabPosition != "bottom"){
10269         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10270          * @type Roo.Element
10271          */
10272         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10273         this.el.addClass("x-tabs-top");
10274     }
10275     this.items = [];
10276
10277     this.bodyEl.setStyle("position", "relative");
10278
10279     this.active = null;
10280     this.activateDelegate = this.activate.createDelegate(this);
10281
10282     this.addEvents({
10283         /**
10284          * @event tabchange
10285          * Fires when the active tab changes
10286          * @param {Roo.TabPanel} this
10287          * @param {Roo.TabPanelItem} activePanel The new active tab
10288          */
10289         "tabchange": true,
10290         /**
10291          * @event beforetabchange
10292          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10293          * @param {Roo.TabPanel} this
10294          * @param {Object} e Set cancel to true on this object to cancel the tab change
10295          * @param {Roo.TabPanelItem} tab The tab being changed to
10296          */
10297         "beforetabchange" : true
10298     });
10299
10300     Roo.EventManager.onWindowResize(this.onResize, this);
10301     this.cpad = this.el.getPadding("lr");
10302     this.hiddenCount = 0;
10303
10304
10305     // toolbar on the tabbar support...
10306     if (this.toolbar) {
10307         var tcfg = this.toolbar;
10308         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10309         this.toolbar = new Roo.Toolbar(tcfg);
10310         if (Roo.isSafari) {
10311             var tbl = tcfg.container.child('table', true);
10312             tbl.setAttribute('width', '100%');
10313         }
10314         
10315     }
10316    
10317
10318
10319     Roo.TabPanel.superclass.constructor.call(this);
10320 };
10321
10322 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10323     /*
10324      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10325      */
10326     tabPosition : "top",
10327     /*
10328      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10329      */
10330     currentTabWidth : 0,
10331     /*
10332      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10333      */
10334     minTabWidth : 40,
10335     /*
10336      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10337      */
10338     maxTabWidth : 250,
10339     /*
10340      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10341      */
10342     preferredTabWidth : 175,
10343     /*
10344      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10345      */
10346     resizeTabs : false,
10347     /*
10348      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10349      */
10350     monitorResize : true,
10351     /*
10352      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10353      */
10354     toolbar : false,
10355
10356     /**
10357      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10358      * @param {String} id The id of the div to use <b>or create</b>
10359      * @param {String} text The text for the tab
10360      * @param {String} content (optional) Content to put in the TabPanelItem body
10361      * @param {Boolean} closable (optional) True to create a close icon on the tab
10362      * @return {Roo.TabPanelItem} The created TabPanelItem
10363      */
10364     addTab : function(id, text, content, closable){
10365         var item = new Roo.TabPanelItem(this, id, text, closable);
10366         this.addTabItem(item);
10367         if(content){
10368             item.setContent(content);
10369         }
10370         return item;
10371     },
10372
10373     /**
10374      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10375      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10376      * @return {Roo.TabPanelItem}
10377      */
10378     getTab : function(id){
10379         return this.items[id];
10380     },
10381
10382     /**
10383      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10384      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10385      */
10386     hideTab : function(id){
10387         var t = this.items[id];
10388         if(!t.isHidden()){
10389            t.setHidden(true);
10390            this.hiddenCount++;
10391            this.autoSizeTabs();
10392         }
10393     },
10394
10395     /**
10396      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10397      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10398      */
10399     unhideTab : function(id){
10400         var t = this.items[id];
10401         if(t.isHidden()){
10402            t.setHidden(false);
10403            this.hiddenCount--;
10404            this.autoSizeTabs();
10405         }
10406     },
10407
10408     /**
10409      * Adds an existing {@link Roo.TabPanelItem}.
10410      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10411      */
10412     addTabItem : function(item){
10413         this.items[item.id] = item;
10414         this.items.push(item);
10415         if(this.resizeTabs){
10416            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10417            this.autoSizeTabs();
10418         }else{
10419             item.autoSize();
10420         }
10421     },
10422
10423     /**
10424      * Removes a {@link Roo.TabPanelItem}.
10425      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10426      */
10427     removeTab : function(id){
10428         var items = this.items;
10429         var tab = items[id];
10430         if(!tab) { return; }
10431         var index = items.indexOf(tab);
10432         if(this.active == tab && items.length > 1){
10433             var newTab = this.getNextAvailable(index);
10434             if(newTab) {
10435                 newTab.activate();
10436             }
10437         }
10438         this.stripEl.dom.removeChild(tab.pnode.dom);
10439         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10440             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10441         }
10442         items.splice(index, 1);
10443         delete this.items[tab.id];
10444         tab.fireEvent("close", tab);
10445         tab.purgeListeners();
10446         this.autoSizeTabs();
10447     },
10448
10449     getNextAvailable : function(start){
10450         var items = this.items;
10451         var index = start;
10452         // look for a next tab that will slide over to
10453         // replace the one being removed
10454         while(index < items.length){
10455             var item = items[++index];
10456             if(item && !item.isHidden()){
10457                 return item;
10458             }
10459         }
10460         // if one isn't found select the previous tab (on the left)
10461         index = start;
10462         while(index >= 0){
10463             var item = items[--index];
10464             if(item && !item.isHidden()){
10465                 return item;
10466             }
10467         }
10468         return null;
10469     },
10470
10471     /**
10472      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10473      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10474      */
10475     disableTab : function(id){
10476         var tab = this.items[id];
10477         if(tab && this.active != tab){
10478             tab.disable();
10479         }
10480     },
10481
10482     /**
10483      * Enables a {@link Roo.TabPanelItem} that is disabled.
10484      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10485      */
10486     enableTab : function(id){
10487         var tab = this.items[id];
10488         tab.enable();
10489     },
10490
10491     /**
10492      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10493      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10494      * @return {Roo.TabPanelItem} The TabPanelItem.
10495      */
10496     activate : function(id){
10497         var tab = this.items[id];
10498         if(!tab){
10499             return null;
10500         }
10501         if(tab == this.active || tab.disabled){
10502             return tab;
10503         }
10504         var e = {};
10505         this.fireEvent("beforetabchange", this, e, tab);
10506         if(e.cancel !== true && !tab.disabled){
10507             if(this.active){
10508                 this.active.hide();
10509             }
10510             this.active = this.items[id];
10511             this.active.show();
10512             this.fireEvent("tabchange", this, this.active);
10513         }
10514         return tab;
10515     },
10516
10517     /**
10518      * Gets the active {@link Roo.TabPanelItem}.
10519      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10520      */
10521     getActiveTab : function(){
10522         return this.active;
10523     },
10524
10525     /**
10526      * Updates the tab body element to fit the height of the container element
10527      * for overflow scrolling
10528      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10529      */
10530     syncHeight : function(targetHeight){
10531         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10532         var bm = this.bodyEl.getMargins();
10533         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10534         this.bodyEl.setHeight(newHeight);
10535         return newHeight;
10536     },
10537
10538     onResize : function(){
10539         if(this.monitorResize){
10540             this.autoSizeTabs();
10541         }
10542     },
10543
10544     /**
10545      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10546      */
10547     beginUpdate : function(){
10548         this.updating = true;
10549     },
10550
10551     /**
10552      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10553      */
10554     endUpdate : function(){
10555         this.updating = false;
10556         this.autoSizeTabs();
10557     },
10558
10559     /**
10560      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10561      */
10562     autoSizeTabs : function(){
10563         var count = this.items.length;
10564         var vcount = count - this.hiddenCount;
10565         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10566         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10567         var availWidth = Math.floor(w / vcount);
10568         var b = this.stripBody;
10569         if(b.getWidth() > w){
10570             var tabs = this.items;
10571             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10572             if(availWidth < this.minTabWidth){
10573                 /*if(!this.sleft){    // incomplete scrolling code
10574                     this.createScrollButtons();
10575                 }
10576                 this.showScroll();
10577                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10578             }
10579         }else{
10580             if(this.currentTabWidth < this.preferredTabWidth){
10581                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10582             }
10583         }
10584     },
10585
10586     /**
10587      * Returns the number of tabs in this TabPanel.
10588      * @return {Number}
10589      */
10590      getCount : function(){
10591          return this.items.length;
10592      },
10593
10594     /**
10595      * Resizes all the tabs to the passed width
10596      * @param {Number} The new width
10597      */
10598     setTabWidth : function(width){
10599         this.currentTabWidth = width;
10600         for(var i = 0, len = this.items.length; i < len; i++) {
10601                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10602         }
10603     },
10604
10605     /**
10606      * Destroys this TabPanel
10607      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10608      */
10609     destroy : function(removeEl){
10610         Roo.EventManager.removeResizeListener(this.onResize, this);
10611         for(var i = 0, len = this.items.length; i < len; i++){
10612             this.items[i].purgeListeners();
10613         }
10614         if(removeEl === true){
10615             this.el.update("");
10616             this.el.remove();
10617         }
10618     }
10619 });
10620
10621 /**
10622  * @class Roo.TabPanelItem
10623  * @extends Roo.util.Observable
10624  * Represents an individual item (tab plus body) in a TabPanel.
10625  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10626  * @param {String} id The id of this TabPanelItem
10627  * @param {String} text The text for the tab of this TabPanelItem
10628  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10629  */
10630 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10631     /**
10632      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10633      * @type Roo.TabPanel
10634      */
10635     this.tabPanel = tabPanel;
10636     /**
10637      * The id for this TabPanelItem
10638      * @type String
10639      */
10640     this.id = id;
10641     /** @private */
10642     this.disabled = false;
10643     /** @private */
10644     this.text = text;
10645     /** @private */
10646     this.loaded = false;
10647     this.closable = closable;
10648
10649     /**
10650      * The body element for this TabPanelItem.
10651      * @type Roo.Element
10652      */
10653     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10654     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10655     this.bodyEl.setStyle("display", "block");
10656     this.bodyEl.setStyle("zoom", "1");
10657     this.hideAction();
10658
10659     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10660     /** @private */
10661     this.el = Roo.get(els.el, true);
10662     this.inner = Roo.get(els.inner, true);
10663     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10664     this.pnode = Roo.get(els.el.parentNode, true);
10665     this.el.on("mousedown", this.onTabMouseDown, this);
10666     this.el.on("click", this.onTabClick, this);
10667     /** @private */
10668     if(closable){
10669         var c = Roo.get(els.close, true);
10670         c.dom.title = this.closeText;
10671         c.addClassOnOver("close-over");
10672         c.on("click", this.closeClick, this);
10673      }
10674
10675     this.addEvents({
10676          /**
10677          * @event activate
10678          * Fires when this tab becomes the active tab.
10679          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10680          * @param {Roo.TabPanelItem} this
10681          */
10682         "activate": true,
10683         /**
10684          * @event beforeclose
10685          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10686          * @param {Roo.TabPanelItem} this
10687          * @param {Object} e Set cancel to true on this object to cancel the close.
10688          */
10689         "beforeclose": true,
10690         /**
10691          * @event close
10692          * Fires when this tab is closed.
10693          * @param {Roo.TabPanelItem} this
10694          */
10695          "close": true,
10696         /**
10697          * @event deactivate
10698          * Fires when this tab is no longer the active tab.
10699          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10700          * @param {Roo.TabPanelItem} this
10701          */
10702          "deactivate" : true
10703     });
10704     this.hidden = false;
10705
10706     Roo.TabPanelItem.superclass.constructor.call(this);
10707 };
10708
10709 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10710     purgeListeners : function(){
10711        Roo.util.Observable.prototype.purgeListeners.call(this);
10712        this.el.removeAllListeners();
10713     },
10714     /**
10715      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10716      */
10717     show : function(){
10718         this.pnode.addClass("on");
10719         this.showAction();
10720         if(Roo.isOpera){
10721             this.tabPanel.stripWrap.repaint();
10722         }
10723         this.fireEvent("activate", this.tabPanel, this);
10724     },
10725
10726     /**
10727      * Returns true if this tab is the active tab.
10728      * @return {Boolean}
10729      */
10730     isActive : function(){
10731         return this.tabPanel.getActiveTab() == this;
10732     },
10733
10734     /**
10735      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10736      */
10737     hide : function(){
10738         this.pnode.removeClass("on");
10739         this.hideAction();
10740         this.fireEvent("deactivate", this.tabPanel, this);
10741     },
10742
10743     hideAction : function(){
10744         this.bodyEl.hide();
10745         this.bodyEl.setStyle("position", "absolute");
10746         this.bodyEl.setLeft("-20000px");
10747         this.bodyEl.setTop("-20000px");
10748     },
10749
10750     showAction : function(){
10751         this.bodyEl.setStyle("position", "relative");
10752         this.bodyEl.setTop("");
10753         this.bodyEl.setLeft("");
10754         this.bodyEl.show();
10755     },
10756
10757     /**
10758      * Set the tooltip for the tab.
10759      * @param {String} tooltip The tab's tooltip
10760      */
10761     setTooltip : function(text){
10762         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10763             this.textEl.dom.qtip = text;
10764             this.textEl.dom.removeAttribute('title');
10765         }else{
10766             this.textEl.dom.title = text;
10767         }
10768     },
10769
10770     onTabClick : function(e){
10771         e.preventDefault();
10772         this.tabPanel.activate(this.id);
10773     },
10774
10775     onTabMouseDown : function(e){
10776         e.preventDefault();
10777         this.tabPanel.activate(this.id);
10778     },
10779
10780     getWidth : function(){
10781         return this.inner.getWidth();
10782     },
10783
10784     setWidth : function(width){
10785         var iwidth = width - this.pnode.getPadding("lr");
10786         this.inner.setWidth(iwidth);
10787         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10788         this.pnode.setWidth(width);
10789     },
10790
10791     /**
10792      * Show or hide the tab
10793      * @param {Boolean} hidden True to hide or false to show.
10794      */
10795     setHidden : function(hidden){
10796         this.hidden = hidden;
10797         this.pnode.setStyle("display", hidden ? "none" : "");
10798     },
10799
10800     /**
10801      * Returns true if this tab is "hidden"
10802      * @return {Boolean}
10803      */
10804     isHidden : function(){
10805         return this.hidden;
10806     },
10807
10808     /**
10809      * Returns the text for this tab
10810      * @return {String}
10811      */
10812     getText : function(){
10813         return this.text;
10814     },
10815
10816     autoSize : function(){
10817         //this.el.beginMeasure();
10818         this.textEl.setWidth(1);
10819         /*
10820          *  #2804 [new] Tabs in Roojs
10821          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10822          */
10823         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10824         //this.el.endMeasure();
10825     },
10826
10827     /**
10828      * Sets the text for the tab (Note: this also sets the tooltip text)
10829      * @param {String} text The tab's text and tooltip
10830      */
10831     setText : function(text){
10832         this.text = text;
10833         this.textEl.update(text);
10834         this.setTooltip(text);
10835         if(!this.tabPanel.resizeTabs){
10836             this.autoSize();
10837         }
10838     },
10839     /**
10840      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10841      */
10842     activate : function(){
10843         this.tabPanel.activate(this.id);
10844     },
10845
10846     /**
10847      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10848      */
10849     disable : function(){
10850         if(this.tabPanel.active != this){
10851             this.disabled = true;
10852             this.pnode.addClass("disabled");
10853         }
10854     },
10855
10856     /**
10857      * Enables this TabPanelItem if it was previously disabled.
10858      */
10859     enable : function(){
10860         this.disabled = false;
10861         this.pnode.removeClass("disabled");
10862     },
10863
10864     /**
10865      * Sets the content for this TabPanelItem.
10866      * @param {String} content The content
10867      * @param {Boolean} loadScripts true to look for and load scripts
10868      */
10869     setContent : function(content, loadScripts){
10870         this.bodyEl.update(content, loadScripts);
10871     },
10872
10873     /**
10874      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10875      * @return {Roo.UpdateManager} The UpdateManager
10876      */
10877     getUpdateManager : function(){
10878         return this.bodyEl.getUpdateManager();
10879     },
10880
10881     /**
10882      * Set a URL to be used to load the content for this TabPanelItem.
10883      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10884      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
10885      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
10886      * @return {Roo.UpdateManager} The UpdateManager
10887      */
10888     setUrl : function(url, params, loadOnce){
10889         if(this.refreshDelegate){
10890             this.un('activate', this.refreshDelegate);
10891         }
10892         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10893         this.on("activate", this.refreshDelegate);
10894         return this.bodyEl.getUpdateManager();
10895     },
10896
10897     /** @private */
10898     _handleRefresh : function(url, params, loadOnce){
10899         if(!loadOnce || !this.loaded){
10900             var updater = this.bodyEl.getUpdateManager();
10901             updater.update(url, params, this._setLoaded.createDelegate(this));
10902         }
10903     },
10904
10905     /**
10906      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10907      *   Will fail silently if the setUrl method has not been called.
10908      *   This does not activate the panel, just updates its content.
10909      */
10910     refresh : function(){
10911         if(this.refreshDelegate){
10912            this.loaded = false;
10913            this.refreshDelegate();
10914         }
10915     },
10916
10917     /** @private */
10918     _setLoaded : function(){
10919         this.loaded = true;
10920     },
10921
10922     /** @private */
10923     closeClick : function(e){
10924         var o = {};
10925         e.stopEvent();
10926         this.fireEvent("beforeclose", this, o);
10927         if(o.cancel !== true){
10928             this.tabPanel.removeTab(this.id);
10929         }
10930     },
10931     /**
10932      * The text displayed in the tooltip for the close icon.
10933      * @type String
10934      */
10935     closeText : "Close this tab"
10936 });
10937
10938 /** @private */
10939 Roo.TabPanel.prototype.createStrip = function(container){
10940     var strip = document.createElement("div");
10941     strip.className = "x-tabs-wrap";
10942     container.appendChild(strip);
10943     return strip;
10944 };
10945 /** @private */
10946 Roo.TabPanel.prototype.createStripList = function(strip){
10947     // div wrapper for retard IE
10948     // returns the "tr" element.
10949     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10950         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10951         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10952     return strip.firstChild.firstChild.firstChild.firstChild;
10953 };
10954 /** @private */
10955 Roo.TabPanel.prototype.createBody = function(container){
10956     var body = document.createElement("div");
10957     Roo.id(body, "tab-body");
10958     Roo.fly(body).addClass("x-tabs-body");
10959     container.appendChild(body);
10960     return body;
10961 };
10962 /** @private */
10963 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10964     var body = Roo.getDom(id);
10965     if(!body){
10966         body = document.createElement("div");
10967         body.id = id;
10968     }
10969     Roo.fly(body).addClass("x-tabs-item-body");
10970     bodyEl.insertBefore(body, bodyEl.firstChild);
10971     return body;
10972 };
10973 /** @private */
10974 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10975     var td = document.createElement("td");
10976     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10977     //stripEl.appendChild(td);
10978     if(closable){
10979         td.className = "x-tabs-closable";
10980         if(!this.closeTpl){
10981             this.closeTpl = new Roo.Template(
10982                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10983                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10984                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10985             );
10986         }
10987         var el = this.closeTpl.overwrite(td, {"text": text});
10988         var close = el.getElementsByTagName("div")[0];
10989         var inner = el.getElementsByTagName("em")[0];
10990         return {"el": el, "close": close, "inner": inner};
10991     } else {
10992         if(!this.tabTpl){
10993             this.tabTpl = new Roo.Template(
10994                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10995                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
10996             );
10997         }
10998         var el = this.tabTpl.overwrite(td, {"text": text});
10999         var inner = el.getElementsByTagName("em")[0];
11000         return {"el": el, "inner": inner};
11001     }
11002 };/*
11003  * Based on:
11004  * Ext JS Library 1.1.1
11005  * Copyright(c) 2006-2007, Ext JS, LLC.
11006  *
11007  * Originally Released Under LGPL - original licence link has changed is not relivant.
11008  *
11009  * Fork - LGPL
11010  * <script type="text/javascript">
11011  */
11012
11013 /**
11014  * @class Roo.Button
11015  * @extends Roo.util.Observable
11016  * Simple Button class
11017  * @cfg {String} text The button text
11018  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11019  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11020  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11021  * @cfg {Object} scope The scope of the handler
11022  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11023  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11024  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11025  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11026  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11027  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11028    applies if enableToggle = true)
11029  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11030  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11031   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11032  * @constructor
11033  * Create a new button
11034  * @param {Object} config The config object
11035  */
11036 Roo.Button = function(renderTo, config)
11037 {
11038     if (!config) {
11039         config = renderTo;
11040         renderTo = config.renderTo || false;
11041     }
11042     
11043     Roo.apply(this, config);
11044     this.addEvents({
11045         /**
11046              * @event click
11047              * Fires when this button is clicked
11048              * @param {Button} this
11049              * @param {EventObject} e The click event
11050              */
11051             "click" : true,
11052         /**
11053              * @event toggle
11054              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11055              * @param {Button} this
11056              * @param {Boolean} pressed
11057              */
11058             "toggle" : true,
11059         /**
11060              * @event mouseover
11061              * Fires when the mouse hovers over the button
11062              * @param {Button} this
11063              * @param {Event} e The event object
11064              */
11065         'mouseover' : true,
11066         /**
11067              * @event mouseout
11068              * Fires when the mouse exits the button
11069              * @param {Button} this
11070              * @param {Event} e The event object
11071              */
11072         'mouseout': true,
11073          /**
11074              * @event render
11075              * Fires when the button is rendered
11076              * @param {Button} this
11077              */
11078         'render': true
11079     });
11080     if(this.menu){
11081         this.menu = Roo.menu.MenuMgr.get(this.menu);
11082     }
11083     // register listeners first!!  - so render can be captured..
11084     Roo.util.Observable.call(this);
11085     if(renderTo){
11086         this.render(renderTo);
11087     }
11088     
11089   
11090 };
11091
11092 Roo.extend(Roo.Button, Roo.util.Observable, {
11093     /**
11094      * 
11095      */
11096     
11097     /**
11098      * Read-only. True if this button is hidden
11099      * @type Boolean
11100      */
11101     hidden : false,
11102     /**
11103      * Read-only. True if this button is disabled
11104      * @type Boolean
11105      */
11106     disabled : false,
11107     /**
11108      * Read-only. True if this button is pressed (only if enableToggle = true)
11109      * @type Boolean
11110      */
11111     pressed : false,
11112
11113     /**
11114      * @cfg {Number} tabIndex 
11115      * The DOM tabIndex for this button (defaults to undefined)
11116      */
11117     tabIndex : undefined,
11118
11119     /**
11120      * @cfg {Boolean} enableToggle
11121      * True to enable pressed/not pressed toggling (defaults to false)
11122      */
11123     enableToggle: false,
11124     /**
11125      * @cfg {Mixed} menu
11126      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11127      */
11128     menu : undefined,
11129     /**
11130      * @cfg {String} menuAlign
11131      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11132      */
11133     menuAlign : "tl-bl?",
11134
11135     /**
11136      * @cfg {String} iconCls
11137      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11138      */
11139     iconCls : undefined,
11140     /**
11141      * @cfg {String} type
11142      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11143      */
11144     type : 'button',
11145
11146     // private
11147     menuClassTarget: 'tr',
11148
11149     /**
11150      * @cfg {String} clickEvent
11151      * The type of event to map to the button's event handler (defaults to 'click')
11152      */
11153     clickEvent : 'click',
11154
11155     /**
11156      * @cfg {Boolean} handleMouseEvents
11157      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11158      */
11159     handleMouseEvents : true,
11160
11161     /**
11162      * @cfg {String} tooltipType
11163      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11164      */
11165     tooltipType : 'qtip',
11166
11167     /**
11168      * @cfg {String} cls
11169      * A CSS class to apply to the button's main element.
11170      */
11171     
11172     /**
11173      * @cfg {Roo.Template} template (Optional)
11174      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11175      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11176      * require code modifications if required elements (e.g. a button) aren't present.
11177      */
11178
11179     // private
11180     render : function(renderTo){
11181         var btn;
11182         if(this.hideParent){
11183             this.parentEl = Roo.get(renderTo);
11184         }
11185         if(!this.dhconfig){
11186             if(!this.template){
11187                 if(!Roo.Button.buttonTemplate){
11188                     // hideous table template
11189                     Roo.Button.buttonTemplate = new Roo.Template(
11190                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11191                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
11192                         "</tr></tbody></table>");
11193                 }
11194                 this.template = Roo.Button.buttonTemplate;
11195             }
11196             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11197             var btnEl = btn.child("button:first");
11198             btnEl.on('focus', this.onFocus, this);
11199             btnEl.on('blur', this.onBlur, this);
11200             if(this.cls){
11201                 btn.addClass(this.cls);
11202             }
11203             if(this.icon){
11204                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11205             }
11206             if(this.iconCls){
11207                 btnEl.addClass(this.iconCls);
11208                 if(!this.cls){
11209                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11210                 }
11211             }
11212             if(this.tabIndex !== undefined){
11213                 btnEl.dom.tabIndex = this.tabIndex;
11214             }
11215             if(this.tooltip){
11216                 if(typeof this.tooltip == 'object'){
11217                     Roo.QuickTips.tips(Roo.apply({
11218                           target: btnEl.id
11219                     }, this.tooltip));
11220                 } else {
11221                     btnEl.dom[this.tooltipType] = this.tooltip;
11222                 }
11223             }
11224         }else{
11225             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11226         }
11227         this.el = btn;
11228         if(this.id){
11229             this.el.dom.id = this.el.id = this.id;
11230         }
11231         if(this.menu){
11232             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11233             this.menu.on("show", this.onMenuShow, this);
11234             this.menu.on("hide", this.onMenuHide, this);
11235         }
11236         btn.addClass("x-btn");
11237         if(Roo.isIE && !Roo.isIE7){
11238             this.autoWidth.defer(1, this);
11239         }else{
11240             this.autoWidth();
11241         }
11242         if(this.handleMouseEvents){
11243             btn.on("mouseover", this.onMouseOver, this);
11244             btn.on("mouseout", this.onMouseOut, this);
11245             btn.on("mousedown", this.onMouseDown, this);
11246         }
11247         btn.on(this.clickEvent, this.onClick, this);
11248         //btn.on("mouseup", this.onMouseUp, this);
11249         if(this.hidden){
11250             this.hide();
11251         }
11252         if(this.disabled){
11253             this.disable();
11254         }
11255         Roo.ButtonToggleMgr.register(this);
11256         if(this.pressed){
11257             this.el.addClass("x-btn-pressed");
11258         }
11259         if(this.repeat){
11260             var repeater = new Roo.util.ClickRepeater(btn,
11261                 typeof this.repeat == "object" ? this.repeat : {}
11262             );
11263             repeater.on("click", this.onClick,  this);
11264         }
11265         
11266         this.fireEvent('render', this);
11267         
11268     },
11269     /**
11270      * Returns the button's underlying element
11271      * @return {Roo.Element} The element
11272      */
11273     getEl : function(){
11274         return this.el;  
11275     },
11276     
11277     /**
11278      * Destroys this Button and removes any listeners.
11279      */
11280     destroy : function(){
11281         Roo.ButtonToggleMgr.unregister(this);
11282         this.el.removeAllListeners();
11283         this.purgeListeners();
11284         this.el.remove();
11285     },
11286
11287     // private
11288     autoWidth : function(){
11289         if(this.el){
11290             this.el.setWidth("auto");
11291             if(Roo.isIE7 && Roo.isStrict){
11292                 var ib = this.el.child('button');
11293                 if(ib && ib.getWidth() > 20){
11294                     ib.clip();
11295                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11296                 }
11297             }
11298             if(this.minWidth){
11299                 if(this.hidden){
11300                     this.el.beginMeasure();
11301                 }
11302                 if(this.el.getWidth() < this.minWidth){
11303                     this.el.setWidth(this.minWidth);
11304                 }
11305                 if(this.hidden){
11306                     this.el.endMeasure();
11307                 }
11308             }
11309         }
11310     },
11311
11312     /**
11313      * Assigns this button's click handler
11314      * @param {Function} handler The function to call when the button is clicked
11315      * @param {Object} scope (optional) Scope for the function passed in
11316      */
11317     setHandler : function(handler, scope){
11318         this.handler = handler;
11319         this.scope = scope;  
11320     },
11321     
11322     /**
11323      * Sets this button's text
11324      * @param {String} text The button text
11325      */
11326     setText : function(text){
11327         this.text = text;
11328         if(this.el){
11329             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11330         }
11331         this.autoWidth();
11332     },
11333     
11334     /**
11335      * Gets the text for this button
11336      * @return {String} The button text
11337      */
11338     getText : function(){
11339         return this.text;  
11340     },
11341     
11342     /**
11343      * Show this button
11344      */
11345     show: function(){
11346         this.hidden = false;
11347         if(this.el){
11348             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11349         }
11350     },
11351     
11352     /**
11353      * Hide this button
11354      */
11355     hide: function(){
11356         this.hidden = true;
11357         if(this.el){
11358             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11359         }
11360     },
11361     
11362     /**
11363      * Convenience function for boolean show/hide
11364      * @param {Boolean} visible True to show, false to hide
11365      */
11366     setVisible: function(visible){
11367         if(visible) {
11368             this.show();
11369         }else{
11370             this.hide();
11371         }
11372     },
11373     
11374     /**
11375      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11376      * @param {Boolean} state (optional) Force a particular state
11377      */
11378     toggle : function(state){
11379         state = state === undefined ? !this.pressed : state;
11380         if(state != this.pressed){
11381             if(state){
11382                 this.el.addClass("x-btn-pressed");
11383                 this.pressed = true;
11384                 this.fireEvent("toggle", this, true);
11385             }else{
11386                 this.el.removeClass("x-btn-pressed");
11387                 this.pressed = false;
11388                 this.fireEvent("toggle", this, false);
11389             }
11390             if(this.toggleHandler){
11391                 this.toggleHandler.call(this.scope || this, this, state);
11392             }
11393         }
11394     },
11395     
11396     /**
11397      * Focus the button
11398      */
11399     focus : function(){
11400         this.el.child('button:first').focus();
11401     },
11402     
11403     /**
11404      * Disable this button
11405      */
11406     disable : function(){
11407         if(this.el){
11408             this.el.addClass("x-btn-disabled");
11409         }
11410         this.disabled = true;
11411     },
11412     
11413     /**
11414      * Enable this button
11415      */
11416     enable : function(){
11417         if(this.el){
11418             this.el.removeClass("x-btn-disabled");
11419         }
11420         this.disabled = false;
11421     },
11422
11423     /**
11424      * Convenience function for boolean enable/disable
11425      * @param {Boolean} enabled True to enable, false to disable
11426      */
11427     setDisabled : function(v){
11428         this[v !== true ? "enable" : "disable"]();
11429     },
11430
11431     // private
11432     onClick : function(e){
11433         if(e){
11434             e.preventDefault();
11435         }
11436         if(e.button != 0){
11437             return;
11438         }
11439         if(!this.disabled){
11440             if(this.enableToggle){
11441                 this.toggle();
11442             }
11443             if(this.menu && !this.menu.isVisible()){
11444                 this.menu.show(this.el, this.menuAlign);
11445             }
11446             this.fireEvent("click", this, e);
11447             if(this.handler){
11448                 this.el.removeClass("x-btn-over");
11449                 this.handler.call(this.scope || this, this, e);
11450             }
11451         }
11452     },
11453     // private
11454     onMouseOver : function(e){
11455         if(!this.disabled){
11456             this.el.addClass("x-btn-over");
11457             this.fireEvent('mouseover', this, e);
11458         }
11459     },
11460     // private
11461     onMouseOut : function(e){
11462         if(!e.within(this.el,  true)){
11463             this.el.removeClass("x-btn-over");
11464             this.fireEvent('mouseout', this, e);
11465         }
11466     },
11467     // private
11468     onFocus : function(e){
11469         if(!this.disabled){
11470             this.el.addClass("x-btn-focus");
11471         }
11472     },
11473     // private
11474     onBlur : function(e){
11475         this.el.removeClass("x-btn-focus");
11476     },
11477     // private
11478     onMouseDown : function(e){
11479         if(!this.disabled && e.button == 0){
11480             this.el.addClass("x-btn-click");
11481             Roo.get(document).on('mouseup', this.onMouseUp, this);
11482         }
11483     },
11484     // private
11485     onMouseUp : function(e){
11486         if(e.button == 0){
11487             this.el.removeClass("x-btn-click");
11488             Roo.get(document).un('mouseup', this.onMouseUp, this);
11489         }
11490     },
11491     // private
11492     onMenuShow : function(e){
11493         this.el.addClass("x-btn-menu-active");
11494     },
11495     // private
11496     onMenuHide : function(e){
11497         this.el.removeClass("x-btn-menu-active");
11498     }   
11499 });
11500
11501 // Private utility class used by Button
11502 Roo.ButtonToggleMgr = function(){
11503    var groups = {};
11504    
11505    function toggleGroup(btn, state){
11506        if(state){
11507            var g = groups[btn.toggleGroup];
11508            for(var i = 0, l = g.length; i < l; i++){
11509                if(g[i] != btn){
11510                    g[i].toggle(false);
11511                }
11512            }
11513        }
11514    }
11515    
11516    return {
11517        register : function(btn){
11518            if(!btn.toggleGroup){
11519                return;
11520            }
11521            var g = groups[btn.toggleGroup];
11522            if(!g){
11523                g = groups[btn.toggleGroup] = [];
11524            }
11525            g.push(btn);
11526            btn.on("toggle", toggleGroup);
11527        },
11528        
11529        unregister : function(btn){
11530            if(!btn.toggleGroup){
11531                return;
11532            }
11533            var g = groups[btn.toggleGroup];
11534            if(g){
11535                g.remove(btn);
11536                btn.un("toggle", toggleGroup);
11537            }
11538        }
11539    };
11540 }();/*
11541  * Based on:
11542  * Ext JS Library 1.1.1
11543  * Copyright(c) 2006-2007, Ext JS, LLC.
11544  *
11545  * Originally Released Under LGPL - original licence link has changed is not relivant.
11546  *
11547  * Fork - LGPL
11548  * <script type="text/javascript">
11549  */
11550  
11551 /**
11552  * @class Roo.SplitButton
11553  * @extends Roo.Button
11554  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11555  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11556  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11557  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11558  * @cfg {String} arrowTooltip The title attribute of the arrow
11559  * @constructor
11560  * Create a new menu button
11561  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11562  * @param {Object} config The config object
11563  */
11564 Roo.SplitButton = function(renderTo, config){
11565     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11566     /**
11567      * @event arrowclick
11568      * Fires when this button's arrow is clicked
11569      * @param {SplitButton} this
11570      * @param {EventObject} e The click event
11571      */
11572     this.addEvents({"arrowclick":true});
11573 };
11574
11575 Roo.extend(Roo.SplitButton, Roo.Button, {
11576     render : function(renderTo){
11577         // this is one sweet looking template!
11578         var tpl = new Roo.Template(
11579             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11580             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11581             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
11582             "</tbody></table></td><td>",
11583             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11584             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
11585             "</tbody></table></td></tr></table>"
11586         );
11587         var btn = tpl.append(renderTo, [this.text, this.type], true);
11588         var btnEl = btn.child("button");
11589         if(this.cls){
11590             btn.addClass(this.cls);
11591         }
11592         if(this.icon){
11593             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11594         }
11595         if(this.iconCls){
11596             btnEl.addClass(this.iconCls);
11597             if(!this.cls){
11598                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11599             }
11600         }
11601         this.el = btn;
11602         if(this.handleMouseEvents){
11603             btn.on("mouseover", this.onMouseOver, this);
11604             btn.on("mouseout", this.onMouseOut, this);
11605             btn.on("mousedown", this.onMouseDown, this);
11606             btn.on("mouseup", this.onMouseUp, this);
11607         }
11608         btn.on(this.clickEvent, this.onClick, this);
11609         if(this.tooltip){
11610             if(typeof this.tooltip == 'object'){
11611                 Roo.QuickTips.tips(Roo.apply({
11612                       target: btnEl.id
11613                 }, this.tooltip));
11614             } else {
11615                 btnEl.dom[this.tooltipType] = this.tooltip;
11616             }
11617         }
11618         if(this.arrowTooltip){
11619             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11620         }
11621         if(this.hidden){
11622             this.hide();
11623         }
11624         if(this.disabled){
11625             this.disable();
11626         }
11627         if(this.pressed){
11628             this.el.addClass("x-btn-pressed");
11629         }
11630         if(Roo.isIE && !Roo.isIE7){
11631             this.autoWidth.defer(1, this);
11632         }else{
11633             this.autoWidth();
11634         }
11635         if(this.menu){
11636             this.menu.on("show", this.onMenuShow, this);
11637             this.menu.on("hide", this.onMenuHide, this);
11638         }
11639         this.fireEvent('render', this);
11640     },
11641
11642     // private
11643     autoWidth : function(){
11644         if(this.el){
11645             var tbl = this.el.child("table:first");
11646             var tbl2 = this.el.child("table:last");
11647             this.el.setWidth("auto");
11648             tbl.setWidth("auto");
11649             if(Roo.isIE7 && Roo.isStrict){
11650                 var ib = this.el.child('button:first');
11651                 if(ib && ib.getWidth() > 20){
11652                     ib.clip();
11653                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11654                 }
11655             }
11656             if(this.minWidth){
11657                 if(this.hidden){
11658                     this.el.beginMeasure();
11659                 }
11660                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11661                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11662                 }
11663                 if(this.hidden){
11664                     this.el.endMeasure();
11665                 }
11666             }
11667             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11668         } 
11669     },
11670     /**
11671      * Sets this button's click handler
11672      * @param {Function} handler The function to call when the button is clicked
11673      * @param {Object} scope (optional) Scope for the function passed above
11674      */
11675     setHandler : function(handler, scope){
11676         this.handler = handler;
11677         this.scope = scope;  
11678     },
11679     
11680     /**
11681      * Sets this button's arrow click handler
11682      * @param {Function} handler The function to call when the arrow is clicked
11683      * @param {Object} scope (optional) Scope for the function passed above
11684      */
11685     setArrowHandler : function(handler, scope){
11686         this.arrowHandler = handler;
11687         this.scope = scope;  
11688     },
11689     
11690     /**
11691      * Focus the button
11692      */
11693     focus : function(){
11694         if(this.el){
11695             this.el.child("button:first").focus();
11696         }
11697     },
11698
11699     // private
11700     onClick : function(e){
11701         e.preventDefault();
11702         if(!this.disabled){
11703             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11704                 if(this.menu && !this.menu.isVisible()){
11705                     this.menu.show(this.el, this.menuAlign);
11706                 }
11707                 this.fireEvent("arrowclick", this, e);
11708                 if(this.arrowHandler){
11709                     this.arrowHandler.call(this.scope || this, this, e);
11710                 }
11711             }else{
11712                 this.fireEvent("click", this, e);
11713                 if(this.handler){
11714                     this.handler.call(this.scope || this, this, e);
11715                 }
11716             }
11717         }
11718     },
11719     // private
11720     onMouseDown : function(e){
11721         if(!this.disabled){
11722             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11723         }
11724     },
11725     // private
11726     onMouseUp : function(e){
11727         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11728     }   
11729 });
11730
11731
11732 // backwards compat
11733 Roo.MenuButton = Roo.SplitButton;/*
11734  * Based on:
11735  * Ext JS Library 1.1.1
11736  * Copyright(c) 2006-2007, Ext JS, LLC.
11737  *
11738  * Originally Released Under LGPL - original licence link has changed is not relivant.
11739  *
11740  * Fork - LGPL
11741  * <script type="text/javascript">
11742  */
11743
11744 /**
11745  * @class Roo.Toolbar
11746  * Basic Toolbar class.
11747  * @constructor
11748  * Creates a new Toolbar
11749  * @param {Object} container The config object
11750  */ 
11751 Roo.Toolbar = function(container, buttons, config)
11752 {
11753     /// old consturctor format still supported..
11754     if(container instanceof Array){ // omit the container for later rendering
11755         buttons = container;
11756         config = buttons;
11757         container = null;
11758     }
11759     if (typeof(container) == 'object' && container.xtype) {
11760         config = container;
11761         container = config.container;
11762         buttons = config.buttons || []; // not really - use items!!
11763     }
11764     var xitems = [];
11765     if (config && config.items) {
11766         xitems = config.items;
11767         delete config.items;
11768     }
11769     Roo.apply(this, config);
11770     this.buttons = buttons;
11771     
11772     if(container){
11773         this.render(container);
11774     }
11775     this.xitems = xitems;
11776     Roo.each(xitems, function(b) {
11777         this.add(b);
11778     }, this);
11779     
11780 };
11781
11782 Roo.Toolbar.prototype = {
11783     /**
11784      * @cfg {Array} items
11785      * array of button configs or elements to add (will be converted to a MixedCollection)
11786      */
11787     
11788     /**
11789      * @cfg {String/HTMLElement/Element} container
11790      * The id or element that will contain the toolbar
11791      */
11792     // private
11793     render : function(ct){
11794         this.el = Roo.get(ct);
11795         if(this.cls){
11796             this.el.addClass(this.cls);
11797         }
11798         // using a table allows for vertical alignment
11799         // 100% width is needed by Safari...
11800         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11801         this.tr = this.el.child("tr", true);
11802         var autoId = 0;
11803         this.items = new Roo.util.MixedCollection(false, function(o){
11804             return o.id || ("item" + (++autoId));
11805         });
11806         if(this.buttons){
11807             this.add.apply(this, this.buttons);
11808             delete this.buttons;
11809         }
11810     },
11811
11812     /**
11813      * Adds element(s) to the toolbar -- this function takes a variable number of 
11814      * arguments of mixed type and adds them to the toolbar.
11815      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11816      * <ul>
11817      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11818      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11819      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11820      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11821      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11822      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11823      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11824      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11825      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11826      * </ul>
11827      * @param {Mixed} arg2
11828      * @param {Mixed} etc.
11829      */
11830     add : function(){
11831         var a = arguments, l = a.length;
11832         for(var i = 0; i < l; i++){
11833             this._add(a[i]);
11834         }
11835     },
11836     // private..
11837     _add : function(el) {
11838         
11839         if (el.xtype) {
11840             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11841         }
11842         
11843         if (el.applyTo){ // some kind of form field
11844             return this.addField(el);
11845         } 
11846         if (el.render){ // some kind of Toolbar.Item
11847             return this.addItem(el);
11848         }
11849         if (typeof el == "string"){ // string
11850             if(el == "separator" || el == "-"){
11851                 return this.addSeparator();
11852             }
11853             if (el == " "){
11854                 return this.addSpacer();
11855             }
11856             if(el == "->"){
11857                 return this.addFill();
11858             }
11859             return this.addText(el);
11860             
11861         }
11862         if(el.tagName){ // element
11863             return this.addElement(el);
11864         }
11865         if(typeof el == "object"){ // must be button config?
11866             return this.addButton(el);
11867         }
11868         // and now what?!?!
11869         return false;
11870         
11871     },
11872     
11873     /**
11874      * Add an Xtype element
11875      * @param {Object} xtype Xtype Object
11876      * @return {Object} created Object
11877      */
11878     addxtype : function(e){
11879         return this.add(e);  
11880     },
11881     
11882     /**
11883      * Returns the Element for this toolbar.
11884      * @return {Roo.Element}
11885      */
11886     getEl : function(){
11887         return this.el;  
11888     },
11889     
11890     /**
11891      * Adds a separator
11892      * @return {Roo.Toolbar.Item} The separator item
11893      */
11894     addSeparator : function(){
11895         return this.addItem(new Roo.Toolbar.Separator());
11896     },
11897
11898     /**
11899      * Adds a spacer element
11900      * @return {Roo.Toolbar.Spacer} The spacer item
11901      */
11902     addSpacer : function(){
11903         return this.addItem(new Roo.Toolbar.Spacer());
11904     },
11905
11906     /**
11907      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11908      * @return {Roo.Toolbar.Fill} The fill item
11909      */
11910     addFill : function(){
11911         return this.addItem(new Roo.Toolbar.Fill());
11912     },
11913
11914     /**
11915      * Adds any standard HTML element to the toolbar
11916      * @param {String/HTMLElement/Element} el The element or id of the element to add
11917      * @return {Roo.Toolbar.Item} The element's item
11918      */
11919     addElement : function(el){
11920         return this.addItem(new Roo.Toolbar.Item(el));
11921     },
11922     /**
11923      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11924      * @type Roo.util.MixedCollection  
11925      */
11926     items : false,
11927      
11928     /**
11929      * Adds any Toolbar.Item or subclass
11930      * @param {Roo.Toolbar.Item} item
11931      * @return {Roo.Toolbar.Item} The item
11932      */
11933     addItem : function(item){
11934         var td = this.nextBlock();
11935         item.render(td);
11936         this.items.add(item);
11937         return item;
11938     },
11939     
11940     /**
11941      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11942      * @param {Object/Array} config A button config or array of configs
11943      * @return {Roo.Toolbar.Button/Array}
11944      */
11945     addButton : function(config){
11946         if(config instanceof Array){
11947             var buttons = [];
11948             for(var i = 0, len = config.length; i < len; i++) {
11949                 buttons.push(this.addButton(config[i]));
11950             }
11951             return buttons;
11952         }
11953         var b = config;
11954         if(!(config instanceof Roo.Toolbar.Button)){
11955             b = config.split ?
11956                 new Roo.Toolbar.SplitButton(config) :
11957                 new Roo.Toolbar.Button(config);
11958         }
11959         var td = this.nextBlock();
11960         b.render(td);
11961         this.items.add(b);
11962         return b;
11963     },
11964     
11965     /**
11966      * Adds text to the toolbar
11967      * @param {String} text The text to add
11968      * @return {Roo.Toolbar.Item} The element's item
11969      */
11970     addText : function(text){
11971         return this.addItem(new Roo.Toolbar.TextItem(text));
11972     },
11973     
11974     /**
11975      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11976      * @param {Number} index The index where the item is to be inserted
11977      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11978      * @return {Roo.Toolbar.Button/Item}
11979      */
11980     insertButton : function(index, item){
11981         if(item instanceof Array){
11982             var buttons = [];
11983             for(var i = 0, len = item.length; i < len; i++) {
11984                buttons.push(this.insertButton(index + i, item[i]));
11985             }
11986             return buttons;
11987         }
11988         if (!(item instanceof Roo.Toolbar.Button)){
11989            item = new Roo.Toolbar.Button(item);
11990         }
11991         var td = document.createElement("td");
11992         this.tr.insertBefore(td, this.tr.childNodes[index]);
11993         item.render(td);
11994         this.items.insert(index, item);
11995         return item;
11996     },
11997     
11998     /**
11999      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12000      * @param {Object} config
12001      * @return {Roo.Toolbar.Item} The element's item
12002      */
12003     addDom : function(config, returnEl){
12004         var td = this.nextBlock();
12005         Roo.DomHelper.overwrite(td, config);
12006         var ti = new Roo.Toolbar.Item(td.firstChild);
12007         ti.render(td);
12008         this.items.add(ti);
12009         return ti;
12010     },
12011
12012     /**
12013      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12014      * @type Roo.util.MixedCollection  
12015      */
12016     fields : false,
12017     
12018     /**
12019      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12020      * Note: the field should not have been rendered yet. For a field that has already been
12021      * rendered, use {@link #addElement}.
12022      * @param {Roo.form.Field} field
12023      * @return {Roo.ToolbarItem}
12024      */
12025      
12026       
12027     addField : function(field) {
12028         if (!this.fields) {
12029             var autoId = 0;
12030             this.fields = new Roo.util.MixedCollection(false, function(o){
12031                 return o.id || ("item" + (++autoId));
12032             });
12033
12034         }
12035         
12036         var td = this.nextBlock();
12037         field.render(td);
12038         var ti = new Roo.Toolbar.Item(td.firstChild);
12039         ti.render(td);
12040         this.items.add(ti);
12041         this.fields.add(field);
12042         return ti;
12043     },
12044     /**
12045      * Hide the toolbar
12046      * @method hide
12047      */
12048      
12049       
12050     hide : function()
12051     {
12052         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12053         this.el.child('div').hide();
12054     },
12055     /**
12056      * Show the toolbar
12057      * @method show
12058      */
12059     show : function()
12060     {
12061         this.el.child('div').show();
12062     },
12063       
12064     // private
12065     nextBlock : function(){
12066         var td = document.createElement("td");
12067         this.tr.appendChild(td);
12068         return td;
12069     },
12070
12071     // private
12072     destroy : function(){
12073         if(this.items){ // rendered?
12074             Roo.destroy.apply(Roo, this.items.items);
12075         }
12076         if(this.fields){ // rendered?
12077             Roo.destroy.apply(Roo, this.fields.items);
12078         }
12079         Roo.Element.uncache(this.el, this.tr);
12080     }
12081 };
12082
12083 /**
12084  * @class Roo.Toolbar.Item
12085  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12086  * @constructor
12087  * Creates a new Item
12088  * @param {HTMLElement} el 
12089  */
12090 Roo.Toolbar.Item = function(el){
12091     var cfg = {};
12092     if (typeof (el.xtype) != 'undefined') {
12093         cfg = el;
12094         el = cfg.el;
12095     }
12096     
12097     this.el = Roo.getDom(el);
12098     this.id = Roo.id(this.el);
12099     this.hidden = false;
12100     
12101     this.addEvents({
12102          /**
12103              * @event render
12104              * Fires when the button is rendered
12105              * @param {Button} this
12106              */
12107         'render': true
12108     });
12109     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
12110 };
12111 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12112 //Roo.Toolbar.Item.prototype = {
12113     
12114     /**
12115      * Get this item's HTML Element
12116      * @return {HTMLElement}
12117      */
12118     getEl : function(){
12119        return this.el;  
12120     },
12121
12122     // private
12123     render : function(td){
12124         
12125          this.td = td;
12126         td.appendChild(this.el);
12127         
12128         this.fireEvent('render', this);
12129     },
12130     
12131     /**
12132      * Removes and destroys this item.
12133      */
12134     destroy : function(){
12135         this.td.parentNode.removeChild(this.td);
12136     },
12137     
12138     /**
12139      * Shows this item.
12140      */
12141     show: function(){
12142         this.hidden = false;
12143         this.td.style.display = "";
12144     },
12145     
12146     /**
12147      * Hides this item.
12148      */
12149     hide: function(){
12150         this.hidden = true;
12151         this.td.style.display = "none";
12152     },
12153     
12154     /**
12155      * Convenience function for boolean show/hide.
12156      * @param {Boolean} visible true to show/false to hide
12157      */
12158     setVisible: function(visible){
12159         if(visible) {
12160             this.show();
12161         }else{
12162             this.hide();
12163         }
12164     },
12165     
12166     /**
12167      * Try to focus this item.
12168      */
12169     focus : function(){
12170         Roo.fly(this.el).focus();
12171     },
12172     
12173     /**
12174      * Disables this item.
12175      */
12176     disable : function(){
12177         Roo.fly(this.td).addClass("x-item-disabled");
12178         this.disabled = true;
12179         this.el.disabled = true;
12180     },
12181     
12182     /**
12183      * Enables this item.
12184      */
12185     enable : function(){
12186         Roo.fly(this.td).removeClass("x-item-disabled");
12187         this.disabled = false;
12188         this.el.disabled = false;
12189     }
12190 });
12191
12192
12193 /**
12194  * @class Roo.Toolbar.Separator
12195  * @extends Roo.Toolbar.Item
12196  * A simple toolbar separator class
12197  * @constructor
12198  * Creates a new Separator
12199  */
12200 Roo.Toolbar.Separator = function(cfg){
12201     
12202     var s = document.createElement("span");
12203     s.className = "ytb-sep";
12204     if (cfg) {
12205         cfg.el = s;
12206     }
12207     
12208     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
12209 };
12210 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12211     enable:Roo.emptyFn,
12212     disable:Roo.emptyFn,
12213     focus:Roo.emptyFn
12214 });
12215
12216 /**
12217  * @class Roo.Toolbar.Spacer
12218  * @extends Roo.Toolbar.Item
12219  * A simple element that adds extra horizontal space to a toolbar.
12220  * @constructor
12221  * Creates a new Spacer
12222  */
12223 Roo.Toolbar.Spacer = function(cfg){
12224     var s = document.createElement("div");
12225     s.className = "ytb-spacer";
12226     if (cfg) {
12227         cfg.el = s;
12228     }
12229     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
12230 };
12231 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12232     enable:Roo.emptyFn,
12233     disable:Roo.emptyFn,
12234     focus:Roo.emptyFn
12235 });
12236
12237 /**
12238  * @class Roo.Toolbar.Fill
12239  * @extends Roo.Toolbar.Spacer
12240  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12241  * @constructor
12242  * Creates a new Spacer
12243  */
12244 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12245     // private
12246     render : function(td){
12247         td.style.width = '100%';
12248         Roo.Toolbar.Fill.superclass.render.call(this, td);
12249     }
12250 });
12251
12252 /**
12253  * @class Roo.Toolbar.TextItem
12254  * @extends Roo.Toolbar.Item
12255  * A simple class that renders text directly into a toolbar.
12256  * @constructor
12257  * Creates a new TextItem
12258  * @param {String} text
12259  */
12260 Roo.Toolbar.TextItem = function(cfg){
12261     var  text = cfg || "";
12262     if (typeof(cfg) == 'object') {
12263         text = cfg.text || "";
12264     }  else {
12265         cfg = null;
12266     }
12267     var s = document.createElement("span");
12268     s.className = "ytb-text";
12269     s.innerHTML = text;
12270     if (cfg) {
12271         cfg.el  = s;
12272     }
12273     
12274     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
12275 };
12276 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12277     
12278      
12279     enable:Roo.emptyFn,
12280     disable:Roo.emptyFn,
12281     focus:Roo.emptyFn
12282 });
12283
12284 /**
12285  * @class Roo.Toolbar.Button
12286  * @extends Roo.Button
12287  * A button that renders into a toolbar.
12288  * @constructor
12289  * Creates a new Button
12290  * @param {Object} config A standard {@link Roo.Button} config object
12291  */
12292 Roo.Toolbar.Button = function(config){
12293     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12294 };
12295 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12296     render : function(td){
12297         this.td = td;
12298         Roo.Toolbar.Button.superclass.render.call(this, td);
12299     },
12300     
12301     /**
12302      * Removes and destroys this button
12303      */
12304     destroy : function(){
12305         Roo.Toolbar.Button.superclass.destroy.call(this);
12306         this.td.parentNode.removeChild(this.td);
12307     },
12308     
12309     /**
12310      * Shows this button
12311      */
12312     show: function(){
12313         this.hidden = false;
12314         this.td.style.display = "";
12315     },
12316     
12317     /**
12318      * Hides this button
12319      */
12320     hide: function(){
12321         this.hidden = true;
12322         this.td.style.display = "none";
12323     },
12324
12325     /**
12326      * Disables this item
12327      */
12328     disable : function(){
12329         Roo.fly(this.td).addClass("x-item-disabled");
12330         this.disabled = true;
12331     },
12332
12333     /**
12334      * Enables this item
12335      */
12336     enable : function(){
12337         Roo.fly(this.td).removeClass("x-item-disabled");
12338         this.disabled = false;
12339     }
12340 });
12341 // backwards compat
12342 Roo.ToolbarButton = Roo.Toolbar.Button;
12343
12344 /**
12345  * @class Roo.Toolbar.SplitButton
12346  * @extends Roo.SplitButton
12347  * A menu button that renders into a toolbar.
12348  * @constructor
12349  * Creates a new SplitButton
12350  * @param {Object} config A standard {@link Roo.SplitButton} config object
12351  */
12352 Roo.Toolbar.SplitButton = function(config){
12353     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12354 };
12355 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12356     render : function(td){
12357         this.td = td;
12358         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12359     },
12360     
12361     /**
12362      * Removes and destroys this button
12363      */
12364     destroy : function(){
12365         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12366         this.td.parentNode.removeChild(this.td);
12367     },
12368     
12369     /**
12370      * Shows this button
12371      */
12372     show: function(){
12373         this.hidden = false;
12374         this.td.style.display = "";
12375     },
12376     
12377     /**
12378      * Hides this button
12379      */
12380     hide: function(){
12381         this.hidden = true;
12382         this.td.style.display = "none";
12383     }
12384 });
12385
12386 // backwards compat
12387 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12388  * Based on:
12389  * Ext JS Library 1.1.1
12390  * Copyright(c) 2006-2007, Ext JS, LLC.
12391  *
12392  * Originally Released Under LGPL - original licence link has changed is not relivant.
12393  *
12394  * Fork - LGPL
12395  * <script type="text/javascript">
12396  */
12397  
12398 /**
12399  * @class Roo.PagingToolbar
12400  * @extends Roo.Toolbar
12401  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12402  * @constructor
12403  * Create a new PagingToolbar
12404  * @param {Object} config The config object
12405  */
12406 Roo.PagingToolbar = function(el, ds, config)
12407 {
12408     // old args format still supported... - xtype is prefered..
12409     if (typeof(el) == 'object' && el.xtype) {
12410         // created from xtype...
12411         config = el;
12412         ds = el.dataSource;
12413         el = config.container;
12414     }
12415     var items = [];
12416     if (config.items) {
12417         items = config.items;
12418         config.items = [];
12419     }
12420     
12421     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12422     this.ds = ds;
12423     this.cursor = 0;
12424     this.renderButtons(this.el);
12425     this.bind(ds);
12426     
12427     // supprot items array.
12428    
12429     Roo.each(items, function(e) {
12430         this.add(Roo.factory(e));
12431     },this);
12432     
12433 };
12434
12435 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12436     /**
12437      * @cfg {Roo.data.Store} dataSource
12438      * The underlying data store providing the paged data
12439      */
12440     /**
12441      * @cfg {String/HTMLElement/Element} container
12442      * container The id or element that will contain the toolbar
12443      */
12444     /**
12445      * @cfg {Boolean} displayInfo
12446      * True to display the displayMsg (defaults to false)
12447      */
12448     /**
12449      * @cfg {Number} pageSize
12450      * The number of records to display per page (defaults to 20)
12451      */
12452     pageSize: 20,
12453     /**
12454      * @cfg {String} displayMsg
12455      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12456      */
12457     displayMsg : 'Displaying {0} - {1} of {2}',
12458     /**
12459      * @cfg {String} emptyMsg
12460      * The message to display when no records are found (defaults to "No data to display")
12461      */
12462     emptyMsg : 'No data to display',
12463     /**
12464      * Customizable piece of the default paging text (defaults to "Page")
12465      * @type String
12466      */
12467     beforePageText : "Page",
12468     /**
12469      * Customizable piece of the default paging text (defaults to "of %0")
12470      * @type String
12471      */
12472     afterPageText : "of {0}",
12473     /**
12474      * Customizable piece of the default paging text (defaults to "First Page")
12475      * @type String
12476      */
12477     firstText : "First Page",
12478     /**
12479      * Customizable piece of the default paging text (defaults to "Previous Page")
12480      * @type String
12481      */
12482     prevText : "Previous Page",
12483     /**
12484      * Customizable piece of the default paging text (defaults to "Next Page")
12485      * @type String
12486      */
12487     nextText : "Next Page",
12488     /**
12489      * Customizable piece of the default paging text (defaults to "Last Page")
12490      * @type String
12491      */
12492     lastText : "Last Page",
12493     /**
12494      * Customizable piece of the default paging text (defaults to "Refresh")
12495      * @type String
12496      */
12497     refreshText : "Refresh",
12498
12499     // private
12500     renderButtons : function(el){
12501         Roo.PagingToolbar.superclass.render.call(this, el);
12502         this.first = this.addButton({
12503             tooltip: this.firstText,
12504             cls: "x-btn-icon x-grid-page-first",
12505             disabled: true,
12506             handler: this.onClick.createDelegate(this, ["first"])
12507         });
12508         this.prev = this.addButton({
12509             tooltip: this.prevText,
12510             cls: "x-btn-icon x-grid-page-prev",
12511             disabled: true,
12512             handler: this.onClick.createDelegate(this, ["prev"])
12513         });
12514         //this.addSeparator();
12515         this.add(this.beforePageText);
12516         this.field = Roo.get(this.addDom({
12517            tag: "input",
12518            type: "text",
12519            size: "3",
12520            value: "1",
12521            cls: "x-grid-page-number"
12522         }).el);
12523         this.field.on("keydown", this.onPagingKeydown, this);
12524         this.field.on("focus", function(){this.dom.select();});
12525         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12526         this.field.setHeight(18);
12527         //this.addSeparator();
12528         this.next = this.addButton({
12529             tooltip: this.nextText,
12530             cls: "x-btn-icon x-grid-page-next",
12531             disabled: true,
12532             handler: this.onClick.createDelegate(this, ["next"])
12533         });
12534         this.last = this.addButton({
12535             tooltip: this.lastText,
12536             cls: "x-btn-icon x-grid-page-last",
12537             disabled: true,
12538             handler: this.onClick.createDelegate(this, ["last"])
12539         });
12540         //this.addSeparator();
12541         this.loading = this.addButton({
12542             tooltip: this.refreshText,
12543             cls: "x-btn-icon x-grid-loading",
12544             handler: this.onClick.createDelegate(this, ["refresh"])
12545         });
12546
12547         if(this.displayInfo){
12548             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12549         }
12550     },
12551
12552     // private
12553     updateInfo : function(){
12554         if(this.displayEl){
12555             var count = this.ds.getCount();
12556             var msg = count == 0 ?
12557                 this.emptyMsg :
12558                 String.format(
12559                     this.displayMsg,
12560                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12561                 );
12562             this.displayEl.update(msg);
12563         }
12564     },
12565
12566     // private
12567     onLoad : function(ds, r, o){
12568        this.cursor = o.params ? o.params.start : 0;
12569        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12570
12571        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12572        this.field.dom.value = ap;
12573        this.first.setDisabled(ap == 1);
12574        this.prev.setDisabled(ap == 1);
12575        this.next.setDisabled(ap == ps);
12576        this.last.setDisabled(ap == ps);
12577        this.loading.enable();
12578        this.updateInfo();
12579     },
12580
12581     // private
12582     getPageData : function(){
12583         var total = this.ds.getTotalCount();
12584         return {
12585             total : total,
12586             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12587             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12588         };
12589     },
12590
12591     // private
12592     onLoadError : function(){
12593         this.loading.enable();
12594     },
12595
12596     // private
12597     onPagingKeydown : function(e){
12598         var k = e.getKey();
12599         var d = this.getPageData();
12600         if(k == e.RETURN){
12601             var v = this.field.dom.value, pageNum;
12602             if(!v || isNaN(pageNum = parseInt(v, 10))){
12603                 this.field.dom.value = d.activePage;
12604                 return;
12605             }
12606             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12607             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12608             e.stopEvent();
12609         }
12610         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))
12611         {
12612           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12613           this.field.dom.value = pageNum;
12614           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12615           e.stopEvent();
12616         }
12617         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12618         {
12619           var v = this.field.dom.value, pageNum; 
12620           var increment = (e.shiftKey) ? 10 : 1;
12621           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12622             increment *= -1;
12623           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12624             this.field.dom.value = d.activePage;
12625             return;
12626           }
12627           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12628           {
12629             this.field.dom.value = parseInt(v, 10) + increment;
12630             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12631             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12632           }
12633           e.stopEvent();
12634         }
12635     },
12636
12637     // private
12638     beforeLoad : function(){
12639         if(this.loading){
12640             this.loading.disable();
12641         }
12642     },
12643
12644     // private
12645     onClick : function(which){
12646         var ds = this.ds;
12647         switch(which){
12648             case "first":
12649                 ds.load({params:{start: 0, limit: this.pageSize}});
12650             break;
12651             case "prev":
12652                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12653             break;
12654             case "next":
12655                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12656             break;
12657             case "last":
12658                 var total = ds.getTotalCount();
12659                 var extra = total % this.pageSize;
12660                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12661                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12662             break;
12663             case "refresh":
12664                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12665             break;
12666         }
12667     },
12668
12669     /**
12670      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12671      * @param {Roo.data.Store} store The data store to unbind
12672      */
12673     unbind : function(ds){
12674         ds.un("beforeload", this.beforeLoad, this);
12675         ds.un("load", this.onLoad, this);
12676         ds.un("loadexception", this.onLoadError, this);
12677         ds.un("remove", this.updateInfo, this);
12678         ds.un("add", this.updateInfo, this);
12679         this.ds = undefined;
12680     },
12681
12682     /**
12683      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12684      * @param {Roo.data.Store} store The data store to bind
12685      */
12686     bind : function(ds){
12687         ds.on("beforeload", this.beforeLoad, this);
12688         ds.on("load", this.onLoad, this);
12689         ds.on("loadexception", this.onLoadError, this);
12690         ds.on("remove", this.updateInfo, this);
12691         ds.on("add", this.updateInfo, this);
12692         this.ds = ds;
12693     }
12694 });/*
12695  * Based on:
12696  * Ext JS Library 1.1.1
12697  * Copyright(c) 2006-2007, Ext JS, LLC.
12698  *
12699  * Originally Released Under LGPL - original licence link has changed is not relivant.
12700  *
12701  * Fork - LGPL
12702  * <script type="text/javascript">
12703  */
12704
12705 /**
12706  * @class Roo.Resizable
12707  * @extends Roo.util.Observable
12708  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12709  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12710  * 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
12711  * the element will be wrapped for you automatically.</p>
12712  * <p>Here is the list of valid resize handles:</p>
12713  * <pre>
12714 Value   Description
12715 ------  -------------------
12716  'n'     north
12717  's'     south
12718  'e'     east
12719  'w'     west
12720  'nw'    northwest
12721  'sw'    southwest
12722  'se'    southeast
12723  'ne'    northeast
12724  'hd'    horizontal drag
12725  'all'   all
12726 </pre>
12727  * <p>Here's an example showing the creation of a typical Resizable:</p>
12728  * <pre><code>
12729 var resizer = new Roo.Resizable("element-id", {
12730     handles: 'all',
12731     minWidth: 200,
12732     minHeight: 100,
12733     maxWidth: 500,
12734     maxHeight: 400,
12735     pinned: true
12736 });
12737 resizer.on("resize", myHandler);
12738 </code></pre>
12739  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12740  * resizer.east.setDisplayed(false);</p>
12741  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12742  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12743  * resize operation's new size (defaults to [0, 0])
12744  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12745  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12746  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12747  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12748  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12749  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12750  * @cfg {Number} width The width of the element in pixels (defaults to null)
12751  * @cfg {Number} height The height of the element in pixels (defaults to null)
12752  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12753  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12754  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12755  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12756  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12757  * in favor of the handles config option (defaults to false)
12758  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12759  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12760  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12761  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12762  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12763  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12764  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12765  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12766  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12767  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12768  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12769  * @constructor
12770  * Create a new resizable component
12771  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12772  * @param {Object} config configuration options
12773   */
12774 Roo.Resizable = function(el, config)
12775 {
12776     this.el = Roo.get(el);
12777
12778     if(config && config.wrap){
12779         config.resizeChild = this.el;
12780         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12781         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12782         this.el.setStyle("overflow", "hidden");
12783         this.el.setPositioning(config.resizeChild.getPositioning());
12784         config.resizeChild.clearPositioning();
12785         if(!config.width || !config.height){
12786             var csize = config.resizeChild.getSize();
12787             this.el.setSize(csize.width, csize.height);
12788         }
12789         if(config.pinned && !config.adjustments){
12790             config.adjustments = "auto";
12791         }
12792     }
12793
12794     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12795     this.proxy.unselectable();
12796     this.proxy.enableDisplayMode('block');
12797
12798     Roo.apply(this, config);
12799
12800     if(this.pinned){
12801         this.disableTrackOver = true;
12802         this.el.addClass("x-resizable-pinned");
12803     }
12804     // if the element isn't positioned, make it relative
12805     var position = this.el.getStyle("position");
12806     if(position != "absolute" && position != "fixed"){
12807         this.el.setStyle("position", "relative");
12808     }
12809     if(!this.handles){ // no handles passed, must be legacy style
12810         this.handles = 's,e,se';
12811         if(this.multiDirectional){
12812             this.handles += ',n,w';
12813         }
12814     }
12815     if(this.handles == "all"){
12816         this.handles = "n s e w ne nw se sw";
12817     }
12818     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12819     var ps = Roo.Resizable.positions;
12820     for(var i = 0, len = hs.length; i < len; i++){
12821         if(hs[i] && ps[hs[i]]){
12822             var pos = ps[hs[i]];
12823             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12824         }
12825     }
12826     // legacy
12827     this.corner = this.southeast;
12828     
12829     // updateBox = the box can move..
12830     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12831         this.updateBox = true;
12832     }
12833
12834     this.activeHandle = null;
12835
12836     if(this.resizeChild){
12837         if(typeof this.resizeChild == "boolean"){
12838             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12839         }else{
12840             this.resizeChild = Roo.get(this.resizeChild, true);
12841         }
12842     }
12843     
12844     if(this.adjustments == "auto"){
12845         var rc = this.resizeChild;
12846         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12847         if(rc && (hw || hn)){
12848             rc.position("relative");
12849             rc.setLeft(hw ? hw.el.getWidth() : 0);
12850             rc.setTop(hn ? hn.el.getHeight() : 0);
12851         }
12852         this.adjustments = [
12853             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12854             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12855         ];
12856     }
12857
12858     if(this.draggable){
12859         this.dd = this.dynamic ?
12860             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12861         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12862     }
12863
12864     // public events
12865     this.addEvents({
12866         /**
12867          * @event beforeresize
12868          * Fired before resize is allowed. Set enabled to false to cancel resize.
12869          * @param {Roo.Resizable} this
12870          * @param {Roo.EventObject} e The mousedown event
12871          */
12872         "beforeresize" : true,
12873         /**
12874          * @event resizing
12875          * Fired a resizing.
12876          * @param {Roo.Resizable} this
12877          * @param {Number} x The new x position
12878          * @param {Number} y The new y position
12879          * @param {Number} w The new w width
12880          * @param {Number} h The new h hight
12881          * @param {Roo.EventObject} e The mouseup event
12882          */
12883         "resizing" : true,
12884         /**
12885          * @event resize
12886          * Fired after a resize.
12887          * @param {Roo.Resizable} this
12888          * @param {Number} width The new width
12889          * @param {Number} height The new height
12890          * @param {Roo.EventObject} e The mouseup event
12891          */
12892         "resize" : true
12893     });
12894
12895     if(this.width !== null && this.height !== null){
12896         this.resizeTo(this.width, this.height);
12897     }else{
12898         this.updateChildSize();
12899     }
12900     if(Roo.isIE){
12901         this.el.dom.style.zoom = 1;
12902     }
12903     Roo.Resizable.superclass.constructor.call(this);
12904 };
12905
12906 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12907         resizeChild : false,
12908         adjustments : [0, 0],
12909         minWidth : 5,
12910         minHeight : 5,
12911         maxWidth : 10000,
12912         maxHeight : 10000,
12913         enabled : true,
12914         animate : false,
12915         duration : .35,
12916         dynamic : false,
12917         handles : false,
12918         multiDirectional : false,
12919         disableTrackOver : false,
12920         easing : 'easeOutStrong',
12921         widthIncrement : 0,
12922         heightIncrement : 0,
12923         pinned : false,
12924         width : null,
12925         height : null,
12926         preserveRatio : false,
12927         transparent: false,
12928         minX: 0,
12929         minY: 0,
12930         draggable: false,
12931
12932         /**
12933          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12934          */
12935         constrainTo: undefined,
12936         /**
12937          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12938          */
12939         resizeRegion: undefined,
12940
12941
12942     /**
12943      * Perform a manual resize
12944      * @param {Number} width
12945      * @param {Number} height
12946      */
12947     resizeTo : function(width, height){
12948         this.el.setSize(width, height);
12949         this.updateChildSize();
12950         this.fireEvent("resize", this, width, height, null);
12951     },
12952
12953     // private
12954     startSizing : function(e, handle){
12955         this.fireEvent("beforeresize", this, e);
12956         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12957
12958             if(!this.overlay){
12959                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12960                 this.overlay.unselectable();
12961                 this.overlay.enableDisplayMode("block");
12962                 this.overlay.on("mousemove", this.onMouseMove, this);
12963                 this.overlay.on("mouseup", this.onMouseUp, this);
12964             }
12965             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12966
12967             this.resizing = true;
12968             this.startBox = this.el.getBox();
12969             this.startPoint = e.getXY();
12970             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12971                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12972
12973             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12974             this.overlay.show();
12975
12976             if(this.constrainTo) {
12977                 var ct = Roo.get(this.constrainTo);
12978                 this.resizeRegion = ct.getRegion().adjust(
12979                     ct.getFrameWidth('t'),
12980                     ct.getFrameWidth('l'),
12981                     -ct.getFrameWidth('b'),
12982                     -ct.getFrameWidth('r')
12983                 );
12984             }
12985
12986             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
12987             this.proxy.show();
12988             this.proxy.setBox(this.startBox);
12989             if(!this.dynamic){
12990                 this.proxy.setStyle('visibility', 'visible');
12991             }
12992         }
12993     },
12994
12995     // private
12996     onMouseDown : function(handle, e){
12997         if(this.enabled){
12998             e.stopEvent();
12999             this.activeHandle = handle;
13000             this.startSizing(e, handle);
13001         }
13002     },
13003
13004     // private
13005     onMouseUp : function(e){
13006         var size = this.resizeElement();
13007         this.resizing = false;
13008         this.handleOut();
13009         this.overlay.hide();
13010         this.proxy.hide();
13011         this.fireEvent("resize", this, size.width, size.height, e);
13012     },
13013
13014     // private
13015     updateChildSize : function(){
13016         
13017         if(this.resizeChild){
13018             var el = this.el;
13019             var child = this.resizeChild;
13020             var adj = this.adjustments;
13021             if(el.dom.offsetWidth){
13022                 var b = el.getSize(true);
13023                 child.setSize(b.width+adj[0], b.height+adj[1]);
13024             }
13025             // Second call here for IE
13026             // The first call enables instant resizing and
13027             // the second call corrects scroll bars if they
13028             // exist
13029             if(Roo.isIE){
13030                 setTimeout(function(){
13031                     if(el.dom.offsetWidth){
13032                         var b = el.getSize(true);
13033                         child.setSize(b.width+adj[0], b.height+adj[1]);
13034                     }
13035                 }, 10);
13036             }
13037         }
13038     },
13039
13040     // private
13041     snap : function(value, inc, min){
13042         if(!inc || !value) return value;
13043         var newValue = value;
13044         var m = value % inc;
13045         if(m > 0){
13046             if(m > (inc/2)){
13047                 newValue = value + (inc-m);
13048             }else{
13049                 newValue = value - m;
13050             }
13051         }
13052         return Math.max(min, newValue);
13053     },
13054
13055     // private
13056     resizeElement : function(){
13057         var box = this.proxy.getBox();
13058         if(this.updateBox){
13059             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13060         }else{
13061             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13062         }
13063         this.updateChildSize();
13064         if(!this.dynamic){
13065             this.proxy.hide();
13066         }
13067         return box;
13068     },
13069
13070     // private
13071     constrain : function(v, diff, m, mx){
13072         if(v - diff < m){
13073             diff = v - m;
13074         }else if(v - diff > mx){
13075             diff = mx - v;
13076         }
13077         return diff;
13078     },
13079
13080     // private
13081     onMouseMove : function(e){
13082         
13083         if(this.enabled){
13084             try{// try catch so if something goes wrong the user doesn't get hung
13085
13086             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13087                 return;
13088             }
13089
13090             //var curXY = this.startPoint;
13091             var curSize = this.curSize || this.startBox;
13092             var x = this.startBox.x, y = this.startBox.y;
13093             var ox = x, oy = y;
13094             var w = curSize.width, h = curSize.height;
13095             var ow = w, oh = h;
13096             var mw = this.minWidth, mh = this.minHeight;
13097             var mxw = this.maxWidth, mxh = this.maxHeight;
13098             var wi = this.widthIncrement;
13099             var hi = this.heightIncrement;
13100
13101             var eventXY = e.getXY();
13102             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13103             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13104
13105             var pos = this.activeHandle.position;
13106
13107             switch(pos){
13108                 case "east":
13109                     w += diffX;
13110                     w = Math.min(Math.max(mw, w), mxw);
13111                     break;
13112              
13113                 case "south":
13114                     h += diffY;
13115                     h = Math.min(Math.max(mh, h), mxh);
13116                     break;
13117                 case "southeast":
13118                     w += diffX;
13119                     h += diffY;
13120                     w = Math.min(Math.max(mw, w), mxw);
13121                     h = Math.min(Math.max(mh, h), mxh);
13122                     break;
13123                 case "north":
13124                     diffY = this.constrain(h, diffY, mh, mxh);
13125                     y += diffY;
13126                     h -= diffY;
13127                     break;
13128                 case "hdrag":
13129                     
13130                     if (wi) {
13131                         var adiffX = Math.abs(diffX);
13132                         var sub = (adiffX % wi); // how much 
13133                         if (sub > (wi/2)) { // far enough to snap
13134                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13135                         } else {
13136                             // remove difference.. 
13137                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13138                         }
13139                     }
13140                     x += diffX;
13141                     x = Math.max(this.minX, x);
13142                     break;
13143                 case "west":
13144                     diffX = this.constrain(w, diffX, mw, mxw);
13145                     x += diffX;
13146                     w -= diffX;
13147                     break;
13148                 case "northeast":
13149                     w += diffX;
13150                     w = Math.min(Math.max(mw, w), mxw);
13151                     diffY = this.constrain(h, diffY, mh, mxh);
13152                     y += diffY;
13153                     h -= diffY;
13154                     break;
13155                 case "northwest":
13156                     diffX = this.constrain(w, diffX, mw, mxw);
13157                     diffY = this.constrain(h, diffY, mh, mxh);
13158                     y += diffY;
13159                     h -= diffY;
13160                     x += diffX;
13161                     w -= diffX;
13162                     break;
13163                case "southwest":
13164                     diffX = this.constrain(w, diffX, mw, mxw);
13165                     h += diffY;
13166                     h = Math.min(Math.max(mh, h), mxh);
13167                     x += diffX;
13168                     w -= diffX;
13169                     break;
13170             }
13171
13172             var sw = this.snap(w, wi, mw);
13173             var sh = this.snap(h, hi, mh);
13174             if(sw != w || sh != h){
13175                 switch(pos){
13176                     case "northeast":
13177                         y -= sh - h;
13178                     break;
13179                     case "north":
13180                         y -= sh - h;
13181                         break;
13182                     case "southwest":
13183                         x -= sw - w;
13184                     break;
13185                     case "west":
13186                         x -= sw - w;
13187                         break;
13188                     case "northwest":
13189                         x -= sw - w;
13190                         y -= sh - h;
13191                     break;
13192                 }
13193                 w = sw;
13194                 h = sh;
13195             }
13196
13197             if(this.preserveRatio){
13198                 switch(pos){
13199                     case "southeast":
13200                     case "east":
13201                         h = oh * (w/ow);
13202                         h = Math.min(Math.max(mh, h), mxh);
13203                         w = ow * (h/oh);
13204                        break;
13205                     case "south":
13206                         w = ow * (h/oh);
13207                         w = Math.min(Math.max(mw, w), mxw);
13208                         h = oh * (w/ow);
13209                         break;
13210                     case "northeast":
13211                         w = ow * (h/oh);
13212                         w = Math.min(Math.max(mw, w), mxw);
13213                         h = oh * (w/ow);
13214                     break;
13215                     case "north":
13216                         var tw = w;
13217                         w = ow * (h/oh);
13218                         w = Math.min(Math.max(mw, w), mxw);
13219                         h = oh * (w/ow);
13220                         x += (tw - w) / 2;
13221                         break;
13222                     case "southwest":
13223                         h = oh * (w/ow);
13224                         h = Math.min(Math.max(mh, h), mxh);
13225                         var tw = w;
13226                         w = ow * (h/oh);
13227                         x += tw - w;
13228                         break;
13229                     case "west":
13230                         var th = h;
13231                         h = oh * (w/ow);
13232                         h = Math.min(Math.max(mh, h), mxh);
13233                         y += (th - h) / 2;
13234                         var tw = w;
13235                         w = ow * (h/oh);
13236                         x += tw - w;
13237                        break;
13238                     case "northwest":
13239                         var tw = w;
13240                         var th = h;
13241                         h = oh * (w/ow);
13242                         h = Math.min(Math.max(mh, h), mxh);
13243                         w = ow * (h/oh);
13244                         y += th - h;
13245                         x += tw - w;
13246                        break;
13247
13248                 }
13249             }
13250             if (pos == 'hdrag') {
13251                 w = ow;
13252             }
13253             this.proxy.setBounds(x, y, w, h);
13254             if(this.dynamic){
13255                 this.resizeElement();
13256             }
13257             }catch(e){}
13258         }
13259         this.fireEvent("resizing", this, x, y, w, h, e);
13260     },
13261
13262     // private
13263     handleOver : function(){
13264         if(this.enabled){
13265             this.el.addClass("x-resizable-over");
13266         }
13267     },
13268
13269     // private
13270     handleOut : function(){
13271         if(!this.resizing){
13272             this.el.removeClass("x-resizable-over");
13273         }
13274     },
13275
13276     /**
13277      * Returns the element this component is bound to.
13278      * @return {Roo.Element}
13279      */
13280     getEl : function(){
13281         return this.el;
13282     },
13283
13284     /**
13285      * Returns the resizeChild element (or null).
13286      * @return {Roo.Element}
13287      */
13288     getResizeChild : function(){
13289         return this.resizeChild;
13290     },
13291     groupHandler : function()
13292     {
13293         
13294     },
13295     /**
13296      * Destroys this resizable. If the element was wrapped and
13297      * removeEl is not true then the element remains.
13298      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13299      */
13300     destroy : function(removeEl){
13301         this.proxy.remove();
13302         if(this.overlay){
13303             this.overlay.removeAllListeners();
13304             this.overlay.remove();
13305         }
13306         var ps = Roo.Resizable.positions;
13307         for(var k in ps){
13308             if(typeof ps[k] != "function" && this[ps[k]]){
13309                 var h = this[ps[k]];
13310                 h.el.removeAllListeners();
13311                 h.el.remove();
13312             }
13313         }
13314         if(removeEl){
13315             this.el.update("");
13316             this.el.remove();
13317         }
13318     }
13319 });
13320
13321 // private
13322 // hash to map config positions to true positions
13323 Roo.Resizable.positions = {
13324     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13325     hd: "hdrag"
13326 };
13327
13328 // private
13329 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13330     if(!this.tpl){
13331         // only initialize the template if resizable is used
13332         var tpl = Roo.DomHelper.createTemplate(
13333             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13334         );
13335         tpl.compile();
13336         Roo.Resizable.Handle.prototype.tpl = tpl;
13337     }
13338     this.position = pos;
13339     this.rz = rz;
13340     // show north drag fro topdra
13341     var handlepos = pos == 'hdrag' ? 'north' : pos;
13342     
13343     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13344     if (pos == 'hdrag') {
13345         this.el.setStyle('cursor', 'pointer');
13346     }
13347     this.el.unselectable();
13348     if(transparent){
13349         this.el.setOpacity(0);
13350     }
13351     this.el.on("mousedown", this.onMouseDown, this);
13352     if(!disableTrackOver){
13353         this.el.on("mouseover", this.onMouseOver, this);
13354         this.el.on("mouseout", this.onMouseOut, this);
13355     }
13356 };
13357
13358 // private
13359 Roo.Resizable.Handle.prototype = {
13360     afterResize : function(rz){
13361         Roo.log('after?');
13362         // do nothing
13363     },
13364     // private
13365     onMouseDown : function(e){
13366         this.rz.onMouseDown(this, e);
13367     },
13368     // private
13369     onMouseOver : function(e){
13370         this.rz.handleOver(this, e);
13371     },
13372     // private
13373     onMouseOut : function(e){
13374         this.rz.handleOut(this, e);
13375     }
13376 };/*
13377  * Based on:
13378  * Ext JS Library 1.1.1
13379  * Copyright(c) 2006-2007, Ext JS, LLC.
13380  *
13381  * Originally Released Under LGPL - original licence link has changed is not relivant.
13382  *
13383  * Fork - LGPL
13384  * <script type="text/javascript">
13385  */
13386
13387 /**
13388  * @class Roo.Editor
13389  * @extends Roo.Component
13390  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13391  * @constructor
13392  * Create a new Editor
13393  * @param {Roo.form.Field} field The Field object (or descendant)
13394  * @param {Object} config The config object
13395  */
13396 Roo.Editor = function(field, config){
13397     Roo.Editor.superclass.constructor.call(this, config);
13398     this.field = field;
13399     this.addEvents({
13400         /**
13401              * @event beforestartedit
13402              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13403              * false from the handler of this event.
13404              * @param {Editor} this
13405              * @param {Roo.Element} boundEl The underlying element bound to this editor
13406              * @param {Mixed} value The field value being set
13407              */
13408         "beforestartedit" : true,
13409         /**
13410              * @event startedit
13411              * Fires when this editor is displayed
13412              * @param {Roo.Element} boundEl The underlying element bound to this editor
13413              * @param {Mixed} value The starting field value
13414              */
13415         "startedit" : true,
13416         /**
13417              * @event beforecomplete
13418              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13419              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13420              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13421              * event will not fire since no edit actually occurred.
13422              * @param {Editor} this
13423              * @param {Mixed} value The current field value
13424              * @param {Mixed} startValue The original field value
13425              */
13426         "beforecomplete" : true,
13427         /**
13428              * @event complete
13429              * Fires after editing is complete and any changed value has been written to the underlying field.
13430              * @param {Editor} this
13431              * @param {Mixed} value The current field value
13432              * @param {Mixed} startValue The original field value
13433              */
13434         "complete" : true,
13435         /**
13436          * @event specialkey
13437          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13438          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13439          * @param {Roo.form.Field} this
13440          * @param {Roo.EventObject} e The event object
13441          */
13442         "specialkey" : true
13443     });
13444 };
13445
13446 Roo.extend(Roo.Editor, Roo.Component, {
13447     /**
13448      * @cfg {Boolean/String} autosize
13449      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13450      * or "height" to adopt the height only (defaults to false)
13451      */
13452     /**
13453      * @cfg {Boolean} revertInvalid
13454      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13455      * validation fails (defaults to true)
13456      */
13457     /**
13458      * @cfg {Boolean} ignoreNoChange
13459      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13460      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13461      * will never be ignored.
13462      */
13463     /**
13464      * @cfg {Boolean} hideEl
13465      * False to keep the bound element visible while the editor is displayed (defaults to true)
13466      */
13467     /**
13468      * @cfg {Mixed} value
13469      * The data value of the underlying field (defaults to "")
13470      */
13471     value : "",
13472     /**
13473      * @cfg {String} alignment
13474      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13475      */
13476     alignment: "c-c?",
13477     /**
13478      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13479      * for bottom-right shadow (defaults to "frame")
13480      */
13481     shadow : "frame",
13482     /**
13483      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13484      */
13485     constrain : false,
13486     /**
13487      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13488      */
13489     completeOnEnter : false,
13490     /**
13491      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13492      */
13493     cancelOnEsc : false,
13494     /**
13495      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13496      */
13497     updateEl : false,
13498
13499     // private
13500     onRender : function(ct, position){
13501         this.el = new Roo.Layer({
13502             shadow: this.shadow,
13503             cls: "x-editor",
13504             parentEl : ct,
13505             shim : this.shim,
13506             shadowOffset:4,
13507             id: this.id,
13508             constrain: this.constrain
13509         });
13510         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13511         if(this.field.msgTarget != 'title'){
13512             this.field.msgTarget = 'qtip';
13513         }
13514         this.field.render(this.el);
13515         if(Roo.isGecko){
13516             this.field.el.dom.setAttribute('autocomplete', 'off');
13517         }
13518         this.field.on("specialkey", this.onSpecialKey, this);
13519         if(this.swallowKeys){
13520             this.field.el.swallowEvent(['keydown','keypress']);
13521         }
13522         this.field.show();
13523         this.field.on("blur", this.onBlur, this);
13524         if(this.field.grow){
13525             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13526         }
13527     },
13528
13529     onSpecialKey : function(field, e)
13530     {
13531         //Roo.log('editor onSpecialKey');
13532         if(this.completeOnEnter && e.getKey() == e.ENTER){
13533             e.stopEvent();
13534             this.completeEdit();
13535             return;
13536         }
13537         // do not fire special key otherwise it might hide close the editor...
13538         if(e.getKey() == e.ENTER){    
13539             return;
13540         }
13541         if(this.cancelOnEsc && e.getKey() == e.ESC){
13542             this.cancelEdit();
13543             return;
13544         } 
13545         this.fireEvent('specialkey', field, e);
13546     
13547     },
13548
13549     /**
13550      * Starts the editing process and shows the editor.
13551      * @param {String/HTMLElement/Element} el The element to edit
13552      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13553       * to the innerHTML of el.
13554      */
13555     startEdit : function(el, value){
13556         if(this.editing){
13557             this.completeEdit();
13558         }
13559         this.boundEl = Roo.get(el);
13560         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13561         if(!this.rendered){
13562             this.render(this.parentEl || document.body);
13563         }
13564         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13565             return;
13566         }
13567         this.startValue = v;
13568         this.field.setValue(v);
13569         if(this.autoSize){
13570             var sz = this.boundEl.getSize();
13571             switch(this.autoSize){
13572                 case "width":
13573                 this.setSize(sz.width,  "");
13574                 break;
13575                 case "height":
13576                 this.setSize("",  sz.height);
13577                 break;
13578                 default:
13579                 this.setSize(sz.width,  sz.height);
13580             }
13581         }
13582         this.el.alignTo(this.boundEl, this.alignment);
13583         this.editing = true;
13584         if(Roo.QuickTips){
13585             Roo.QuickTips.disable();
13586         }
13587         this.show();
13588     },
13589
13590     /**
13591      * Sets the height and width of this editor.
13592      * @param {Number} width The new width
13593      * @param {Number} height The new height
13594      */
13595     setSize : function(w, h){
13596         this.field.setSize(w, h);
13597         if(this.el){
13598             this.el.sync();
13599         }
13600     },
13601
13602     /**
13603      * Realigns the editor to the bound field based on the current alignment config value.
13604      */
13605     realign : function(){
13606         this.el.alignTo(this.boundEl, this.alignment);
13607     },
13608
13609     /**
13610      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13611      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13612      */
13613     completeEdit : function(remainVisible){
13614         if(!this.editing){
13615             return;
13616         }
13617         var v = this.getValue();
13618         if(this.revertInvalid !== false && !this.field.isValid()){
13619             v = this.startValue;
13620             this.cancelEdit(true);
13621         }
13622         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13623             this.editing = false;
13624             this.hide();
13625             return;
13626         }
13627         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13628             this.editing = false;
13629             if(this.updateEl && this.boundEl){
13630                 this.boundEl.update(v);
13631             }
13632             if(remainVisible !== true){
13633                 this.hide();
13634             }
13635             this.fireEvent("complete", this, v, this.startValue);
13636         }
13637     },
13638
13639     // private
13640     onShow : function(){
13641         this.el.show();
13642         if(this.hideEl !== false){
13643             this.boundEl.hide();
13644         }
13645         this.field.show();
13646         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13647             this.fixIEFocus = true;
13648             this.deferredFocus.defer(50, this);
13649         }else{
13650             this.field.focus();
13651         }
13652         this.fireEvent("startedit", this.boundEl, this.startValue);
13653     },
13654
13655     deferredFocus : function(){
13656         if(this.editing){
13657             this.field.focus();
13658         }
13659     },
13660
13661     /**
13662      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13663      * reverted to the original starting value.
13664      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13665      * cancel (defaults to false)
13666      */
13667     cancelEdit : function(remainVisible){
13668         if(this.editing){
13669             this.setValue(this.startValue);
13670             if(remainVisible !== true){
13671                 this.hide();
13672             }
13673         }
13674     },
13675
13676     // private
13677     onBlur : function(){
13678         if(this.allowBlur !== true && this.editing){
13679             this.completeEdit();
13680         }
13681     },
13682
13683     // private
13684     onHide : function(){
13685         if(this.editing){
13686             this.completeEdit();
13687             return;
13688         }
13689         this.field.blur();
13690         if(this.field.collapse){
13691             this.field.collapse();
13692         }
13693         this.el.hide();
13694         if(this.hideEl !== false){
13695             this.boundEl.show();
13696         }
13697         if(Roo.QuickTips){
13698             Roo.QuickTips.enable();
13699         }
13700     },
13701
13702     /**
13703      * Sets the data value of the editor
13704      * @param {Mixed} value Any valid value supported by the underlying field
13705      */
13706     setValue : function(v){
13707         this.field.setValue(v);
13708     },
13709
13710     /**
13711      * Gets the data value of the editor
13712      * @return {Mixed} The data value
13713      */
13714     getValue : function(){
13715         return this.field.getValue();
13716     }
13717 });/*
13718  * Based on:
13719  * Ext JS Library 1.1.1
13720  * Copyright(c) 2006-2007, Ext JS, LLC.
13721  *
13722  * Originally Released Under LGPL - original licence link has changed is not relivant.
13723  *
13724  * Fork - LGPL
13725  * <script type="text/javascript">
13726  */
13727  
13728 /**
13729  * @class Roo.BasicDialog
13730  * @extends Roo.util.Observable
13731  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13732  * <pre><code>
13733 var dlg = new Roo.BasicDialog("my-dlg", {
13734     height: 200,
13735     width: 300,
13736     minHeight: 100,
13737     minWidth: 150,
13738     modal: true,
13739     proxyDrag: true,
13740     shadow: true
13741 });
13742 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13743 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13744 dlg.addButton('Cancel', dlg.hide, dlg);
13745 dlg.show();
13746 </code></pre>
13747   <b>A Dialog should always be a direct child of the body element.</b>
13748  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13749  * @cfg {String} title Default text to display in the title bar (defaults to null)
13750  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13751  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13752  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13753  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13754  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13755  * (defaults to null with no animation)
13756  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13757  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13758  * property for valid values (defaults to 'all')
13759  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13760  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13761  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13762  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13763  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13764  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13765  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13766  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13767  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13768  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13769  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13770  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13771  * draggable = true (defaults to false)
13772  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13773  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13774  * shadow (defaults to false)
13775  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13776  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13777  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13778  * @cfg {Array} buttons Array of buttons
13779  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13780  * @constructor
13781  * Create a new BasicDialog.
13782  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13783  * @param {Object} config Configuration options
13784  */
13785 Roo.BasicDialog = function(el, config){
13786     this.el = Roo.get(el);
13787     var dh = Roo.DomHelper;
13788     if(!this.el && config && config.autoCreate){
13789         if(typeof config.autoCreate == "object"){
13790             if(!config.autoCreate.id){
13791                 config.autoCreate.id = el;
13792             }
13793             this.el = dh.append(document.body,
13794                         config.autoCreate, true);
13795         }else{
13796             this.el = dh.append(document.body,
13797                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13798         }
13799     }
13800     el = this.el;
13801     el.setDisplayed(true);
13802     el.hide = this.hideAction;
13803     this.id = el.id;
13804     el.addClass("x-dlg");
13805
13806     Roo.apply(this, config);
13807
13808     this.proxy = el.createProxy("x-dlg-proxy");
13809     this.proxy.hide = this.hideAction;
13810     this.proxy.setOpacity(.5);
13811     this.proxy.hide();
13812
13813     if(config.width){
13814         el.setWidth(config.width);
13815     }
13816     if(config.height){
13817         el.setHeight(config.height);
13818     }
13819     this.size = el.getSize();
13820     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13821         this.xy = [config.x,config.y];
13822     }else{
13823         this.xy = el.getCenterXY(true);
13824     }
13825     /** The header element @type Roo.Element */
13826     this.header = el.child("> .x-dlg-hd");
13827     /** The body element @type Roo.Element */
13828     this.body = el.child("> .x-dlg-bd");
13829     /** The footer element @type Roo.Element */
13830     this.footer = el.child("> .x-dlg-ft");
13831
13832     if(!this.header){
13833         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13834     }
13835     if(!this.body){
13836         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13837     }
13838
13839     this.header.unselectable();
13840     if(this.title){
13841         this.header.update(this.title);
13842     }
13843     // this element allows the dialog to be focused for keyboard event
13844     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13845     this.focusEl.swallowEvent("click", true);
13846
13847     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13848
13849     // wrap the body and footer for special rendering
13850     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13851     if(this.footer){
13852         this.bwrap.dom.appendChild(this.footer.dom);
13853     }
13854
13855     this.bg = this.el.createChild({
13856         tag: "div", cls:"x-dlg-bg",
13857         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13858     });
13859     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13860
13861
13862     if(this.autoScroll !== false && !this.autoTabs){
13863         this.body.setStyle("overflow", "auto");
13864     }
13865
13866     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13867
13868     if(this.closable !== false){
13869         this.el.addClass("x-dlg-closable");
13870         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13871         this.close.on("click", this.closeClick, this);
13872         this.close.addClassOnOver("x-dlg-close-over");
13873     }
13874     if(this.collapsible !== false){
13875         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13876         this.collapseBtn.on("click", this.collapseClick, this);
13877         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13878         this.header.on("dblclick", this.collapseClick, this);
13879     }
13880     if(this.resizable !== false){
13881         this.el.addClass("x-dlg-resizable");
13882         this.resizer = new Roo.Resizable(el, {
13883             minWidth: this.minWidth || 80,
13884             minHeight:this.minHeight || 80,
13885             handles: this.resizeHandles || "all",
13886             pinned: true
13887         });
13888         this.resizer.on("beforeresize", this.beforeResize, this);
13889         this.resizer.on("resize", this.onResize, this);
13890     }
13891     if(this.draggable !== false){
13892         el.addClass("x-dlg-draggable");
13893         if (!this.proxyDrag) {
13894             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13895         }
13896         else {
13897             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13898         }
13899         dd.setHandleElId(this.header.id);
13900         dd.endDrag = this.endMove.createDelegate(this);
13901         dd.startDrag = this.startMove.createDelegate(this);
13902         dd.onDrag = this.onDrag.createDelegate(this);
13903         dd.scroll = false;
13904         this.dd = dd;
13905     }
13906     if(this.modal){
13907         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13908         this.mask.enableDisplayMode("block");
13909         this.mask.hide();
13910         this.el.addClass("x-dlg-modal");
13911     }
13912     if(this.shadow){
13913         this.shadow = new Roo.Shadow({
13914             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13915             offset : this.shadowOffset
13916         });
13917     }else{
13918         this.shadowOffset = 0;
13919     }
13920     if(Roo.useShims && this.shim !== false){
13921         this.shim = this.el.createShim();
13922         this.shim.hide = this.hideAction;
13923         this.shim.hide();
13924     }else{
13925         this.shim = false;
13926     }
13927     if(this.autoTabs){
13928         this.initTabs();
13929     }
13930     if (this.buttons) { 
13931         var bts= this.buttons;
13932         this.buttons = [];
13933         Roo.each(bts, function(b) {
13934             this.addButton(b);
13935         }, this);
13936     }
13937     
13938     
13939     this.addEvents({
13940         /**
13941          * @event keydown
13942          * Fires when a key is pressed
13943          * @param {Roo.BasicDialog} this
13944          * @param {Roo.EventObject} e
13945          */
13946         "keydown" : true,
13947         /**
13948          * @event move
13949          * Fires when this dialog is moved by the user.
13950          * @param {Roo.BasicDialog} this
13951          * @param {Number} x The new page X
13952          * @param {Number} y The new page Y
13953          */
13954         "move" : true,
13955         /**
13956          * @event resize
13957          * Fires when this dialog is resized by the user.
13958          * @param {Roo.BasicDialog} this
13959          * @param {Number} width The new width
13960          * @param {Number} height The new height
13961          */
13962         "resize" : true,
13963         /**
13964          * @event beforehide
13965          * Fires before this dialog is hidden.
13966          * @param {Roo.BasicDialog} this
13967          */
13968         "beforehide" : true,
13969         /**
13970          * @event hide
13971          * Fires when this dialog is hidden.
13972          * @param {Roo.BasicDialog} this
13973          */
13974         "hide" : true,
13975         /**
13976          * @event beforeshow
13977          * Fires before this dialog is shown.
13978          * @param {Roo.BasicDialog} this
13979          */
13980         "beforeshow" : true,
13981         /**
13982          * @event show
13983          * Fires when this dialog is shown.
13984          * @param {Roo.BasicDialog} this
13985          */
13986         "show" : true
13987     });
13988     el.on("keydown", this.onKeyDown, this);
13989     el.on("mousedown", this.toFront, this);
13990     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
13991     this.el.hide();
13992     Roo.DialogManager.register(this);
13993     Roo.BasicDialog.superclass.constructor.call(this);
13994 };
13995
13996 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
13997     shadowOffset: Roo.isIE ? 6 : 5,
13998     minHeight: 80,
13999     minWidth: 200,
14000     minButtonWidth: 75,
14001     defaultButton: null,
14002     buttonAlign: "right",
14003     tabTag: 'div',
14004     firstShow: true,
14005
14006     /**
14007      * Sets the dialog title text
14008      * @param {String} text The title text to display
14009      * @return {Roo.BasicDialog} this
14010      */
14011     setTitle : function(text){
14012         this.header.update(text);
14013         return this;
14014     },
14015
14016     // private
14017     closeClick : function(){
14018         this.hide();
14019     },
14020
14021     // private
14022     collapseClick : function(){
14023         this[this.collapsed ? "expand" : "collapse"]();
14024     },
14025
14026     /**
14027      * Collapses the dialog to its minimized state (only the title bar is visible).
14028      * Equivalent to the user clicking the collapse dialog button.
14029      */
14030     collapse : function(){
14031         if(!this.collapsed){
14032             this.collapsed = true;
14033             this.el.addClass("x-dlg-collapsed");
14034             this.restoreHeight = this.el.getHeight();
14035             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14036         }
14037     },
14038
14039     /**
14040      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14041      * clicking the expand dialog button.
14042      */
14043     expand : function(){
14044         if(this.collapsed){
14045             this.collapsed = false;
14046             this.el.removeClass("x-dlg-collapsed");
14047             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14048         }
14049     },
14050
14051     /**
14052      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14053      * @return {Roo.TabPanel} The tabs component
14054      */
14055     initTabs : function(){
14056         var tabs = this.getTabs();
14057         while(tabs.getTab(0)){
14058             tabs.removeTab(0);
14059         }
14060         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14061             var dom = el.dom;
14062             tabs.addTab(Roo.id(dom), dom.title);
14063             dom.title = "";
14064         });
14065         tabs.activate(0);
14066         return tabs;
14067     },
14068
14069     // private
14070     beforeResize : function(){
14071         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14072     },
14073
14074     // private
14075     onResize : function(){
14076         this.refreshSize();
14077         this.syncBodyHeight();
14078         this.adjustAssets();
14079         this.focus();
14080         this.fireEvent("resize", this, this.size.width, this.size.height);
14081     },
14082
14083     // private
14084     onKeyDown : function(e){
14085         if(this.isVisible()){
14086             this.fireEvent("keydown", this, e);
14087         }
14088     },
14089
14090     /**
14091      * Resizes the dialog.
14092      * @param {Number} width
14093      * @param {Number} height
14094      * @return {Roo.BasicDialog} this
14095      */
14096     resizeTo : function(width, height){
14097         this.el.setSize(width, height);
14098         this.size = {width: width, height: height};
14099         this.syncBodyHeight();
14100         if(this.fixedcenter){
14101             this.center();
14102         }
14103         if(this.isVisible()){
14104             this.constrainXY();
14105             this.adjustAssets();
14106         }
14107         this.fireEvent("resize", this, width, height);
14108         return this;
14109     },
14110
14111
14112     /**
14113      * Resizes the dialog to fit the specified content size.
14114      * @param {Number} width
14115      * @param {Number} height
14116      * @return {Roo.BasicDialog} this
14117      */
14118     setContentSize : function(w, h){
14119         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14120         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14121         //if(!this.el.isBorderBox()){
14122             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14123             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14124         //}
14125         if(this.tabs){
14126             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14127             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14128         }
14129         this.resizeTo(w, h);
14130         return this;
14131     },
14132
14133     /**
14134      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14135      * executed in response to a particular key being pressed while the dialog is active.
14136      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14137      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14138      * @param {Function} fn The function to call
14139      * @param {Object} scope (optional) The scope of the function
14140      * @return {Roo.BasicDialog} this
14141      */
14142     addKeyListener : function(key, fn, scope){
14143         var keyCode, shift, ctrl, alt;
14144         if(typeof key == "object" && !(key instanceof Array)){
14145             keyCode = key["key"];
14146             shift = key["shift"];
14147             ctrl = key["ctrl"];
14148             alt = key["alt"];
14149         }else{
14150             keyCode = key;
14151         }
14152         var handler = function(dlg, e){
14153             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14154                 var k = e.getKey();
14155                 if(keyCode instanceof Array){
14156                     for(var i = 0, len = keyCode.length; i < len; i++){
14157                         if(keyCode[i] == k){
14158                           fn.call(scope || window, dlg, k, e);
14159                           return;
14160                         }
14161                     }
14162                 }else{
14163                     if(k == keyCode){
14164                         fn.call(scope || window, dlg, k, e);
14165                     }
14166                 }
14167             }
14168         };
14169         this.on("keydown", handler);
14170         return this;
14171     },
14172
14173     /**
14174      * Returns the TabPanel component (creates it if it doesn't exist).
14175      * Note: If you wish to simply check for the existence of tabs without creating them,
14176      * check for a null 'tabs' property.
14177      * @return {Roo.TabPanel} The tabs component
14178      */
14179     getTabs : function(){
14180         if(!this.tabs){
14181             this.el.addClass("x-dlg-auto-tabs");
14182             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14183             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14184         }
14185         return this.tabs;
14186     },
14187
14188     /**
14189      * Adds a button to the footer section of the dialog.
14190      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14191      * object or a valid Roo.DomHelper element config
14192      * @param {Function} handler The function called when the button is clicked
14193      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14194      * @return {Roo.Button} The new button
14195      */
14196     addButton : function(config, handler, scope){
14197         var dh = Roo.DomHelper;
14198         if(!this.footer){
14199             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14200         }
14201         if(!this.btnContainer){
14202             var tb = this.footer.createChild({
14203
14204                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14205                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14206             }, null, true);
14207             this.btnContainer = tb.firstChild.firstChild.firstChild;
14208         }
14209         var bconfig = {
14210             handler: handler,
14211             scope: scope,
14212             minWidth: this.minButtonWidth,
14213             hideParent:true
14214         };
14215         if(typeof config == "string"){
14216             bconfig.text = config;
14217         }else{
14218             if(config.tag){
14219                 bconfig.dhconfig = config;
14220             }else{
14221                 Roo.apply(bconfig, config);
14222             }
14223         }
14224         var fc = false;
14225         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14226             bconfig.position = Math.max(0, bconfig.position);
14227             fc = this.btnContainer.childNodes[bconfig.position];
14228         }
14229          
14230         var btn = new Roo.Button(
14231             fc ? 
14232                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14233                 : this.btnContainer.appendChild(document.createElement("td")),
14234             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14235             bconfig
14236         );
14237         this.syncBodyHeight();
14238         if(!this.buttons){
14239             /**
14240              * Array of all the buttons that have been added to this dialog via addButton
14241              * @type Array
14242              */
14243             this.buttons = [];
14244         }
14245         this.buttons.push(btn);
14246         return btn;
14247     },
14248
14249     /**
14250      * Sets the default button to be focused when the dialog is displayed.
14251      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14252      * @return {Roo.BasicDialog} this
14253      */
14254     setDefaultButton : function(btn){
14255         this.defaultButton = btn;
14256         return this;
14257     },
14258
14259     // private
14260     getHeaderFooterHeight : function(safe){
14261         var height = 0;
14262         if(this.header){
14263            height += this.header.getHeight();
14264         }
14265         if(this.footer){
14266            var fm = this.footer.getMargins();
14267             height += (this.footer.getHeight()+fm.top+fm.bottom);
14268         }
14269         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14270         height += this.centerBg.getPadding("tb");
14271         return height;
14272     },
14273
14274     // private
14275     syncBodyHeight : function()
14276     {
14277         var bd = this.body, // the text
14278             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14279             bw = this.bwrap;
14280         var height = this.size.height - this.getHeaderFooterHeight(false);
14281         bd.setHeight(height-bd.getMargins("tb"));
14282         var hh = this.header.getHeight();
14283         var h = this.size.height-hh;
14284         cb.setHeight(h);
14285         
14286         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14287         bw.setHeight(h-cb.getPadding("tb"));
14288         
14289         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14290         bd.setWidth(bw.getWidth(true));
14291         if(this.tabs){
14292             this.tabs.syncHeight();
14293             if(Roo.isIE){
14294                 this.tabs.el.repaint();
14295             }
14296         }
14297     },
14298
14299     /**
14300      * Restores the previous state of the dialog if Roo.state is configured.
14301      * @return {Roo.BasicDialog} this
14302      */
14303     restoreState : function(){
14304         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14305         if(box && box.width){
14306             this.xy = [box.x, box.y];
14307             this.resizeTo(box.width, box.height);
14308         }
14309         return this;
14310     },
14311
14312     // private
14313     beforeShow : function(){
14314         this.expand();
14315         if(this.fixedcenter){
14316             this.xy = this.el.getCenterXY(true);
14317         }
14318         if(this.modal){
14319             Roo.get(document.body).addClass("x-body-masked");
14320             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14321             this.mask.show();
14322         }
14323         this.constrainXY();
14324     },
14325
14326     // private
14327     animShow : function(){
14328         var b = Roo.get(this.animateTarget).getBox();
14329         this.proxy.setSize(b.width, b.height);
14330         this.proxy.setLocation(b.x, b.y);
14331         this.proxy.show();
14332         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14333                     true, .35, this.showEl.createDelegate(this));
14334     },
14335
14336     /**
14337      * Shows the dialog.
14338      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14339      * @return {Roo.BasicDialog} this
14340      */
14341     show : function(animateTarget){
14342         if (this.fireEvent("beforeshow", this) === false){
14343             return;
14344         }
14345         if(this.syncHeightBeforeShow){
14346             this.syncBodyHeight();
14347         }else if(this.firstShow){
14348             this.firstShow = false;
14349             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14350         }
14351         this.animateTarget = animateTarget || this.animateTarget;
14352         if(!this.el.isVisible()){
14353             this.beforeShow();
14354             if(this.animateTarget && Roo.get(this.animateTarget)){
14355                 this.animShow();
14356             }else{
14357                 this.showEl();
14358             }
14359         }
14360         return this;
14361     },
14362
14363     // private
14364     showEl : function(){
14365         this.proxy.hide();
14366         this.el.setXY(this.xy);
14367         this.el.show();
14368         this.adjustAssets(true);
14369         this.toFront();
14370         this.focus();
14371         // IE peekaboo bug - fix found by Dave Fenwick
14372         if(Roo.isIE){
14373             this.el.repaint();
14374         }
14375         this.fireEvent("show", this);
14376     },
14377
14378     /**
14379      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14380      * dialog itself will receive focus.
14381      */
14382     focus : function(){
14383         if(this.defaultButton){
14384             this.defaultButton.focus();
14385         }else{
14386             this.focusEl.focus();
14387         }
14388     },
14389
14390     // private
14391     constrainXY : function(){
14392         if(this.constraintoviewport !== false){
14393             if(!this.viewSize){
14394                 if(this.container){
14395                     var s = this.container.getSize();
14396                     this.viewSize = [s.width, s.height];
14397                 }else{
14398                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14399                 }
14400             }
14401             var s = Roo.get(this.container||document).getScroll();
14402
14403             var x = this.xy[0], y = this.xy[1];
14404             var w = this.size.width, h = this.size.height;
14405             var vw = this.viewSize[0], vh = this.viewSize[1];
14406             // only move it if it needs it
14407             var moved = false;
14408             // first validate right/bottom
14409             if(x + w > vw+s.left){
14410                 x = vw - w;
14411                 moved = true;
14412             }
14413             if(y + h > vh+s.top){
14414                 y = vh - h;
14415                 moved = true;
14416             }
14417             // then make sure top/left isn't negative
14418             if(x < s.left){
14419                 x = s.left;
14420                 moved = true;
14421             }
14422             if(y < s.top){
14423                 y = s.top;
14424                 moved = true;
14425             }
14426             if(moved){
14427                 // cache xy
14428                 this.xy = [x, y];
14429                 if(this.isVisible()){
14430                     this.el.setLocation(x, y);
14431                     this.adjustAssets();
14432                 }
14433             }
14434         }
14435     },
14436
14437     // private
14438     onDrag : function(){
14439         if(!this.proxyDrag){
14440             this.xy = this.el.getXY();
14441             this.adjustAssets();
14442         }
14443     },
14444
14445     // private
14446     adjustAssets : function(doShow){
14447         var x = this.xy[0], y = this.xy[1];
14448         var w = this.size.width, h = this.size.height;
14449         if(doShow === true){
14450             if(this.shadow){
14451                 this.shadow.show(this.el);
14452             }
14453             if(this.shim){
14454                 this.shim.show();
14455             }
14456         }
14457         if(this.shadow && this.shadow.isVisible()){
14458             this.shadow.show(this.el);
14459         }
14460         if(this.shim && this.shim.isVisible()){
14461             this.shim.setBounds(x, y, w, h);
14462         }
14463     },
14464
14465     // private
14466     adjustViewport : function(w, h){
14467         if(!w || !h){
14468             w = Roo.lib.Dom.getViewWidth();
14469             h = Roo.lib.Dom.getViewHeight();
14470         }
14471         // cache the size
14472         this.viewSize = [w, h];
14473         if(this.modal && this.mask.isVisible()){
14474             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14475             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14476         }
14477         if(this.isVisible()){
14478             this.constrainXY();
14479         }
14480     },
14481
14482     /**
14483      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14484      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14485      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14486      */
14487     destroy : function(removeEl){
14488         if(this.isVisible()){
14489             this.animateTarget = null;
14490             this.hide();
14491         }
14492         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14493         if(this.tabs){
14494             this.tabs.destroy(removeEl);
14495         }
14496         Roo.destroy(
14497              this.shim,
14498              this.proxy,
14499              this.resizer,
14500              this.close,
14501              this.mask
14502         );
14503         if(this.dd){
14504             this.dd.unreg();
14505         }
14506         if(this.buttons){
14507            for(var i = 0, len = this.buttons.length; i < len; i++){
14508                this.buttons[i].destroy();
14509            }
14510         }
14511         this.el.removeAllListeners();
14512         if(removeEl === true){
14513             this.el.update("");
14514             this.el.remove();
14515         }
14516         Roo.DialogManager.unregister(this);
14517     },
14518
14519     // private
14520     startMove : function(){
14521         if(this.proxyDrag){
14522             this.proxy.show();
14523         }
14524         if(this.constraintoviewport !== false){
14525             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14526         }
14527     },
14528
14529     // private
14530     endMove : function(){
14531         if(!this.proxyDrag){
14532             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14533         }else{
14534             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14535             this.proxy.hide();
14536         }
14537         this.refreshSize();
14538         this.adjustAssets();
14539         this.focus();
14540         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14541     },
14542
14543     /**
14544      * Brings this dialog to the front of any other visible dialogs
14545      * @return {Roo.BasicDialog} this
14546      */
14547     toFront : function(){
14548         Roo.DialogManager.bringToFront(this);
14549         return this;
14550     },
14551
14552     /**
14553      * Sends this dialog to the back (under) of any other visible dialogs
14554      * @return {Roo.BasicDialog} this
14555      */
14556     toBack : function(){
14557         Roo.DialogManager.sendToBack(this);
14558         return this;
14559     },
14560
14561     /**
14562      * Centers this dialog in the viewport
14563      * @return {Roo.BasicDialog} this
14564      */
14565     center : function(){
14566         var xy = this.el.getCenterXY(true);
14567         this.moveTo(xy[0], xy[1]);
14568         return this;
14569     },
14570
14571     /**
14572      * Moves the dialog's top-left corner to the specified point
14573      * @param {Number} x
14574      * @param {Number} y
14575      * @return {Roo.BasicDialog} this
14576      */
14577     moveTo : function(x, y){
14578         this.xy = [x,y];
14579         if(this.isVisible()){
14580             this.el.setXY(this.xy);
14581             this.adjustAssets();
14582         }
14583         return this;
14584     },
14585
14586     /**
14587      * Aligns the dialog to the specified element
14588      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14589      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14590      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14591      * @return {Roo.BasicDialog} this
14592      */
14593     alignTo : function(element, position, offsets){
14594         this.xy = this.el.getAlignToXY(element, position, offsets);
14595         if(this.isVisible()){
14596             this.el.setXY(this.xy);
14597             this.adjustAssets();
14598         }
14599         return this;
14600     },
14601
14602     /**
14603      * Anchors an element to another element and realigns it when the window is resized.
14604      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14605      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14606      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14607      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14608      * is a number, it is used as the buffer delay (defaults to 50ms).
14609      * @return {Roo.BasicDialog} this
14610      */
14611     anchorTo : function(el, alignment, offsets, monitorScroll){
14612         var action = function(){
14613             this.alignTo(el, alignment, offsets);
14614         };
14615         Roo.EventManager.onWindowResize(action, this);
14616         var tm = typeof monitorScroll;
14617         if(tm != 'undefined'){
14618             Roo.EventManager.on(window, 'scroll', action, this,
14619                 {buffer: tm == 'number' ? monitorScroll : 50});
14620         }
14621         action.call(this);
14622         return this;
14623     },
14624
14625     /**
14626      * Returns true if the dialog is visible
14627      * @return {Boolean}
14628      */
14629     isVisible : function(){
14630         return this.el.isVisible();
14631     },
14632
14633     // private
14634     animHide : function(callback){
14635         var b = Roo.get(this.animateTarget).getBox();
14636         this.proxy.show();
14637         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14638         this.el.hide();
14639         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14640                     this.hideEl.createDelegate(this, [callback]));
14641     },
14642
14643     /**
14644      * Hides the dialog.
14645      * @param {Function} callback (optional) Function to call when the dialog is hidden
14646      * @return {Roo.BasicDialog} this
14647      */
14648     hide : function(callback){
14649         if (this.fireEvent("beforehide", this) === false){
14650             return;
14651         }
14652         if(this.shadow){
14653             this.shadow.hide();
14654         }
14655         if(this.shim) {
14656           this.shim.hide();
14657         }
14658         // sometimes animateTarget seems to get set.. causing problems...
14659         // this just double checks..
14660         if(this.animateTarget && Roo.get(this.animateTarget)) {
14661            this.animHide(callback);
14662         }else{
14663             this.el.hide();
14664             this.hideEl(callback);
14665         }
14666         return this;
14667     },
14668
14669     // private
14670     hideEl : function(callback){
14671         this.proxy.hide();
14672         if(this.modal){
14673             this.mask.hide();
14674             Roo.get(document.body).removeClass("x-body-masked");
14675         }
14676         this.fireEvent("hide", this);
14677         if(typeof callback == "function"){
14678             callback();
14679         }
14680     },
14681
14682     // private
14683     hideAction : function(){
14684         this.setLeft("-10000px");
14685         this.setTop("-10000px");
14686         this.setStyle("visibility", "hidden");
14687     },
14688
14689     // private
14690     refreshSize : function(){
14691         this.size = this.el.getSize();
14692         this.xy = this.el.getXY();
14693         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14694     },
14695
14696     // private
14697     // z-index is managed by the DialogManager and may be overwritten at any time
14698     setZIndex : function(index){
14699         if(this.modal){
14700             this.mask.setStyle("z-index", index);
14701         }
14702         if(this.shim){
14703             this.shim.setStyle("z-index", ++index);
14704         }
14705         if(this.shadow){
14706             this.shadow.setZIndex(++index);
14707         }
14708         this.el.setStyle("z-index", ++index);
14709         if(this.proxy){
14710             this.proxy.setStyle("z-index", ++index);
14711         }
14712         if(this.resizer){
14713             this.resizer.proxy.setStyle("z-index", ++index);
14714         }
14715
14716         this.lastZIndex = index;
14717     },
14718
14719     /**
14720      * Returns the element for this dialog
14721      * @return {Roo.Element} The underlying dialog Element
14722      */
14723     getEl : function(){
14724         return this.el;
14725     }
14726 });
14727
14728 /**
14729  * @class Roo.DialogManager
14730  * Provides global access to BasicDialogs that have been created and
14731  * support for z-indexing (layering) multiple open dialogs.
14732  */
14733 Roo.DialogManager = function(){
14734     var list = {};
14735     var accessList = [];
14736     var front = null;
14737
14738     // private
14739     var sortDialogs = function(d1, d2){
14740         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14741     };
14742
14743     // private
14744     var orderDialogs = function(){
14745         accessList.sort(sortDialogs);
14746         var seed = Roo.DialogManager.zseed;
14747         for(var i = 0, len = accessList.length; i < len; i++){
14748             var dlg = accessList[i];
14749             if(dlg){
14750                 dlg.setZIndex(seed + (i*10));
14751             }
14752         }
14753     };
14754
14755     return {
14756         /**
14757          * The starting z-index for BasicDialogs (defaults to 9000)
14758          * @type Number The z-index value
14759          */
14760         zseed : 9000,
14761
14762         // private
14763         register : function(dlg){
14764             list[dlg.id] = dlg;
14765             accessList.push(dlg);
14766         },
14767
14768         // private
14769         unregister : function(dlg){
14770             delete list[dlg.id];
14771             var i=0;
14772             var len=0;
14773             if(!accessList.indexOf){
14774                 for(  i = 0, len = accessList.length; i < len; i++){
14775                     if(accessList[i] == dlg){
14776                         accessList.splice(i, 1);
14777                         return;
14778                     }
14779                 }
14780             }else{
14781                  i = accessList.indexOf(dlg);
14782                 if(i != -1){
14783                     accessList.splice(i, 1);
14784                 }
14785             }
14786         },
14787
14788         /**
14789          * Gets a registered dialog by id
14790          * @param {String/Object} id The id of the dialog or a dialog
14791          * @return {Roo.BasicDialog} this
14792          */
14793         get : function(id){
14794             return typeof id == "object" ? id : list[id];
14795         },
14796
14797         /**
14798          * Brings the specified dialog to the front
14799          * @param {String/Object} dlg The id of the dialog or a dialog
14800          * @return {Roo.BasicDialog} this
14801          */
14802         bringToFront : function(dlg){
14803             dlg = this.get(dlg);
14804             if(dlg != front){
14805                 front = dlg;
14806                 dlg._lastAccess = new Date().getTime();
14807                 orderDialogs();
14808             }
14809             return dlg;
14810         },
14811
14812         /**
14813          * Sends the specified dialog to the back
14814          * @param {String/Object} dlg The id of the dialog or a dialog
14815          * @return {Roo.BasicDialog} this
14816          */
14817         sendToBack : function(dlg){
14818             dlg = this.get(dlg);
14819             dlg._lastAccess = -(new Date().getTime());
14820             orderDialogs();
14821             return dlg;
14822         },
14823
14824         /**
14825          * Hides all dialogs
14826          */
14827         hideAll : function(){
14828             for(var id in list){
14829                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14830                     list[id].hide();
14831                 }
14832             }
14833         }
14834     };
14835 }();
14836
14837 /**
14838  * @class Roo.LayoutDialog
14839  * @extends Roo.BasicDialog
14840  * Dialog which provides adjustments for working with a layout in a Dialog.
14841  * Add your necessary layout config options to the dialog's config.<br>
14842  * Example usage (including a nested layout):
14843  * <pre><code>
14844 if(!dialog){
14845     dialog = new Roo.LayoutDialog("download-dlg", {
14846         modal: true,
14847         width:600,
14848         height:450,
14849         shadow:true,
14850         minWidth:500,
14851         minHeight:350,
14852         autoTabs:true,
14853         proxyDrag:true,
14854         // layout config merges with the dialog config
14855         center:{
14856             tabPosition: "top",
14857             alwaysShowTabs: true
14858         }
14859     });
14860     dialog.addKeyListener(27, dialog.hide, dialog);
14861     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14862     dialog.addButton("Build It!", this.getDownload, this);
14863
14864     // we can even add nested layouts
14865     var innerLayout = new Roo.BorderLayout("dl-inner", {
14866         east: {
14867             initialSize: 200,
14868             autoScroll:true,
14869             split:true
14870         },
14871         center: {
14872             autoScroll:true
14873         }
14874     });
14875     innerLayout.beginUpdate();
14876     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14877     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14878     innerLayout.endUpdate(true);
14879
14880     var layout = dialog.getLayout();
14881     layout.beginUpdate();
14882     layout.add("center", new Roo.ContentPanel("standard-panel",
14883                         {title: "Download the Source", fitToFrame:true}));
14884     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14885                {title: "Build your own roo.js"}));
14886     layout.getRegion("center").showPanel(sp);
14887     layout.endUpdate();
14888 }
14889 </code></pre>
14890     * @constructor
14891     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14892     * @param {Object} config configuration options
14893   */
14894 Roo.LayoutDialog = function(el, cfg){
14895     
14896     var config=  cfg;
14897     if (typeof(cfg) == 'undefined') {
14898         config = Roo.apply({}, el);
14899         // not sure why we use documentElement here.. - it should always be body.
14900         // IE7 borks horribly if we use documentElement.
14901         // webkit also does not like documentElement - it creates a body element...
14902         el = Roo.get( document.body || document.documentElement ).createChild();
14903         //config.autoCreate = true;
14904     }
14905     
14906     
14907     config.autoTabs = false;
14908     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14909     this.body.setStyle({overflow:"hidden", position:"relative"});
14910     this.layout = new Roo.BorderLayout(this.body.dom, config);
14911     this.layout.monitorWindowResize = false;
14912     this.el.addClass("x-dlg-auto-layout");
14913     // fix case when center region overwrites center function
14914     this.center = Roo.BasicDialog.prototype.center;
14915     this.on("show", this.layout.layout, this.layout, true);
14916     if (config.items) {
14917         var xitems = config.items;
14918         delete config.items;
14919         Roo.each(xitems, this.addxtype, this);
14920     }
14921     
14922     
14923 };
14924 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14925     /**
14926      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14927      * @deprecated
14928      */
14929     endUpdate : function(){
14930         this.layout.endUpdate();
14931     },
14932
14933     /**
14934      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14935      *  @deprecated
14936      */
14937     beginUpdate : function(){
14938         this.layout.beginUpdate();
14939     },
14940
14941     /**
14942      * Get the BorderLayout for this dialog
14943      * @return {Roo.BorderLayout}
14944      */
14945     getLayout : function(){
14946         return this.layout;
14947     },
14948
14949     showEl : function(){
14950         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14951         if(Roo.isIE7){
14952             this.layout.layout();
14953         }
14954     },
14955
14956     // private
14957     // Use the syncHeightBeforeShow config option to control this automatically
14958     syncBodyHeight : function(){
14959         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14960         if(this.layout){this.layout.layout();}
14961     },
14962     
14963       /**
14964      * Add an xtype element (actually adds to the layout.)
14965      * @return {Object} xdata xtype object data.
14966      */
14967     
14968     addxtype : function(c) {
14969         return this.layout.addxtype(c);
14970     }
14971 });/*
14972  * Based on:
14973  * Ext JS Library 1.1.1
14974  * Copyright(c) 2006-2007, Ext JS, LLC.
14975  *
14976  * Originally Released Under LGPL - original licence link has changed is not relivant.
14977  *
14978  * Fork - LGPL
14979  * <script type="text/javascript">
14980  */
14981  
14982 /**
14983  * @class Roo.MessageBox
14984  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
14985  * Example usage:
14986  *<pre><code>
14987 // Basic alert:
14988 Roo.Msg.alert('Status', 'Changes saved successfully.');
14989
14990 // Prompt for user data:
14991 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
14992     if (btn == 'ok'){
14993         // process text value...
14994     }
14995 });
14996
14997 // Show a dialog using config options:
14998 Roo.Msg.show({
14999    title:'Save Changes?',
15000    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15001    buttons: Roo.Msg.YESNOCANCEL,
15002    fn: processResult,
15003    animEl: 'elId'
15004 });
15005 </code></pre>
15006  * @singleton
15007  */
15008 Roo.MessageBox = function(){
15009     var dlg, opt, mask, waitTimer;
15010     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15011     var buttons, activeTextEl, bwidth;
15012
15013     // private
15014     var handleButton = function(button){
15015         dlg.hide();
15016         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15017     };
15018
15019     // private
15020     var handleHide = function(){
15021         if(opt && opt.cls){
15022             dlg.el.removeClass(opt.cls);
15023         }
15024         if(waitTimer){
15025             Roo.TaskMgr.stop(waitTimer);
15026             waitTimer = null;
15027         }
15028     };
15029
15030     // private
15031     var updateButtons = function(b){
15032         var width = 0;
15033         if(!b){
15034             buttons["ok"].hide();
15035             buttons["cancel"].hide();
15036             buttons["yes"].hide();
15037             buttons["no"].hide();
15038             dlg.footer.dom.style.display = 'none';
15039             return width;
15040         }
15041         dlg.footer.dom.style.display = '';
15042         for(var k in buttons){
15043             if(typeof buttons[k] != "function"){
15044                 if(b[k]){
15045                     buttons[k].show();
15046                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15047                     width += buttons[k].el.getWidth()+15;
15048                 }else{
15049                     buttons[k].hide();
15050                 }
15051             }
15052         }
15053         return width;
15054     };
15055
15056     // private
15057     var handleEsc = function(d, k, e){
15058         if(opt && opt.closable !== false){
15059             dlg.hide();
15060         }
15061         if(e){
15062             e.stopEvent();
15063         }
15064     };
15065
15066     return {
15067         /**
15068          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15069          * @return {Roo.BasicDialog} The BasicDialog element
15070          */
15071         getDialog : function(){
15072            if(!dlg){
15073                 dlg = new Roo.BasicDialog("x-msg-box", {
15074                     autoCreate : true,
15075                     shadow: true,
15076                     draggable: true,
15077                     resizable:false,
15078                     constraintoviewport:false,
15079                     fixedcenter:true,
15080                     collapsible : false,
15081                     shim:true,
15082                     modal: true,
15083                     width:400, height:100,
15084                     buttonAlign:"center",
15085                     closeClick : function(){
15086                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15087                             handleButton("no");
15088                         }else{
15089                             handleButton("cancel");
15090                         }
15091                     }
15092                 });
15093                 dlg.on("hide", handleHide);
15094                 mask = dlg.mask;
15095                 dlg.addKeyListener(27, handleEsc);
15096                 buttons = {};
15097                 var bt = this.buttonText;
15098                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15099                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15100                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15101                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15102                 bodyEl = dlg.body.createChild({
15103
15104                     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>'
15105                 });
15106                 msgEl = bodyEl.dom.firstChild;
15107                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15108                 textboxEl.enableDisplayMode();
15109                 textboxEl.addKeyListener([10,13], function(){
15110                     if(dlg.isVisible() && opt && opt.buttons){
15111                         if(opt.buttons.ok){
15112                             handleButton("ok");
15113                         }else if(opt.buttons.yes){
15114                             handleButton("yes");
15115                         }
15116                     }
15117                 });
15118                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15119                 textareaEl.enableDisplayMode();
15120                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15121                 progressEl.enableDisplayMode();
15122                 var pf = progressEl.dom.firstChild;
15123                 if (pf) {
15124                     pp = Roo.get(pf.firstChild);
15125                     pp.setHeight(pf.offsetHeight);
15126                 }
15127                 
15128             }
15129             return dlg;
15130         },
15131
15132         /**
15133          * Updates the message box body text
15134          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15135          * the XHTML-compliant non-breaking space character '&amp;#160;')
15136          * @return {Roo.MessageBox} This message box
15137          */
15138         updateText : function(text){
15139             if(!dlg.isVisible() && !opt.width){
15140                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15141             }
15142             msgEl.innerHTML = text || '&#160;';
15143       
15144             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15145             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15146             var w = Math.max(
15147                     Math.min(opt.width || cw , this.maxWidth), 
15148                     Math.max(opt.minWidth || this.minWidth, bwidth)
15149             );
15150             if(opt.prompt){
15151                 activeTextEl.setWidth(w);
15152             }
15153             if(dlg.isVisible()){
15154                 dlg.fixedcenter = false;
15155             }
15156             // to big, make it scroll. = But as usual stupid IE does not support
15157             // !important..
15158             
15159             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15160                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15161                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15162             } else {
15163                 bodyEl.dom.style.height = '';
15164                 bodyEl.dom.style.overflowY = '';
15165             }
15166             if (cw > w) {
15167                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15168             } else {
15169                 bodyEl.dom.style.overflowX = '';
15170             }
15171             
15172             dlg.setContentSize(w, bodyEl.getHeight());
15173             if(dlg.isVisible()){
15174                 dlg.fixedcenter = true;
15175             }
15176             return this;
15177         },
15178
15179         /**
15180          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15181          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15182          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15183          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15184          * @return {Roo.MessageBox} This message box
15185          */
15186         updateProgress : function(value, text){
15187             if(text){
15188                 this.updateText(text);
15189             }
15190             if (pp) { // weird bug on my firefox - for some reason this is not defined
15191                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15192             }
15193             return this;
15194         },        
15195
15196         /**
15197          * Returns true if the message box is currently displayed
15198          * @return {Boolean} True if the message box is visible, else false
15199          */
15200         isVisible : function(){
15201             return dlg && dlg.isVisible();  
15202         },
15203
15204         /**
15205          * Hides the message box if it is displayed
15206          */
15207         hide : function(){
15208             if(this.isVisible()){
15209                 dlg.hide();
15210             }  
15211         },
15212
15213         /**
15214          * Displays a new message box, or reinitializes an existing message box, based on the config options
15215          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15216          * The following config object properties are supported:
15217          * <pre>
15218 Property    Type             Description
15219 ----------  ---------------  ------------------------------------------------------------------------------------
15220 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15221                                    closes (defaults to undefined)
15222 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15223                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15224 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15225                                    progress and wait dialogs will ignore this property and always hide the
15226                                    close button as they can only be closed programmatically.
15227 cls               String           A custom CSS class to apply to the message box element
15228 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15229                                    displayed (defaults to 75)
15230 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15231                                    function will be btn (the name of the button that was clicked, if applicable,
15232                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15233                                    Progress and wait dialogs will ignore this option since they do not respond to
15234                                    user actions and can only be closed programmatically, so any required function
15235                                    should be called by the same code after it closes the dialog.
15236 icon              String           A CSS class that provides a background image to be used as an icon for
15237                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15238 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15239 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15240 modal             Boolean          False to allow user interaction with the page while the message box is
15241                                    displayed (defaults to true)
15242 msg               String           A string that will replace the existing message box body text (defaults
15243                                    to the XHTML-compliant non-breaking space character '&#160;')
15244 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15245 progress          Boolean          True to display a progress bar (defaults to false)
15246 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15247 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15248 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15249 title             String           The title text
15250 value             String           The string value to set into the active textbox element if displayed
15251 wait              Boolean          True to display a progress bar (defaults to false)
15252 width             Number           The width of the dialog in pixels
15253 </pre>
15254          *
15255          * Example usage:
15256          * <pre><code>
15257 Roo.Msg.show({
15258    title: 'Address',
15259    msg: 'Please enter your address:',
15260    width: 300,
15261    buttons: Roo.MessageBox.OKCANCEL,
15262    multiline: true,
15263    fn: saveAddress,
15264    animEl: 'addAddressBtn'
15265 });
15266 </code></pre>
15267          * @param {Object} config Configuration options
15268          * @return {Roo.MessageBox} This message box
15269          */
15270         show : function(options)
15271         {
15272             
15273             // this causes nightmares if you show one dialog after another
15274             // especially on callbacks..
15275              
15276             if(this.isVisible()){
15277                 
15278                 this.hide();
15279                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15280                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15281                 Roo.log("New Dialog Message:" +  options.msg )
15282                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15283                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15284                 
15285             }
15286             var d = this.getDialog();
15287             opt = options;
15288             d.setTitle(opt.title || "&#160;");
15289             d.close.setDisplayed(opt.closable !== false);
15290             activeTextEl = textboxEl;
15291             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15292             if(opt.prompt){
15293                 if(opt.multiline){
15294                     textboxEl.hide();
15295                     textareaEl.show();
15296                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15297                         opt.multiline : this.defaultTextHeight);
15298                     activeTextEl = textareaEl;
15299                 }else{
15300                     textboxEl.show();
15301                     textareaEl.hide();
15302                 }
15303             }else{
15304                 textboxEl.hide();
15305                 textareaEl.hide();
15306             }
15307             progressEl.setDisplayed(opt.progress === true);
15308             this.updateProgress(0);
15309             activeTextEl.dom.value = opt.value || "";
15310             if(opt.prompt){
15311                 dlg.setDefaultButton(activeTextEl);
15312             }else{
15313                 var bs = opt.buttons;
15314                 var db = null;
15315                 if(bs && bs.ok){
15316                     db = buttons["ok"];
15317                 }else if(bs && bs.yes){
15318                     db = buttons["yes"];
15319                 }
15320                 dlg.setDefaultButton(db);
15321             }
15322             bwidth = updateButtons(opt.buttons);
15323             this.updateText(opt.msg);
15324             if(opt.cls){
15325                 d.el.addClass(opt.cls);
15326             }
15327             d.proxyDrag = opt.proxyDrag === true;
15328             d.modal = opt.modal !== false;
15329             d.mask = opt.modal !== false ? mask : false;
15330             if(!d.isVisible()){
15331                 // force it to the end of the z-index stack so it gets a cursor in FF
15332                 document.body.appendChild(dlg.el.dom);
15333                 d.animateTarget = null;
15334                 d.show(options.animEl);
15335             }
15336             return this;
15337         },
15338
15339         /**
15340          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15341          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15342          * and closing the message box when the process is complete.
15343          * @param {String} title The title bar text
15344          * @param {String} msg The message box body text
15345          * @return {Roo.MessageBox} This message box
15346          */
15347         progress : function(title, msg){
15348             this.show({
15349                 title : title,
15350                 msg : msg,
15351                 buttons: false,
15352                 progress:true,
15353                 closable:false,
15354                 minWidth: this.minProgressWidth,
15355                 modal : true
15356             });
15357             return this;
15358         },
15359
15360         /**
15361          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15362          * If a callback function is passed it will be called after the user clicks the button, and the
15363          * id of the button that was clicked will be passed as the only parameter to the callback
15364          * (could also be the top-right close button).
15365          * @param {String} title The title bar text
15366          * @param {String} msg The message box body text
15367          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15368          * @param {Object} scope (optional) The scope of the callback function
15369          * @return {Roo.MessageBox} This message box
15370          */
15371         alert : function(title, msg, fn, scope){
15372             this.show({
15373                 title : title,
15374                 msg : msg,
15375                 buttons: this.OK,
15376                 fn: fn,
15377                 scope : scope,
15378                 modal : true
15379             });
15380             return this;
15381         },
15382
15383         /**
15384          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15385          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15386          * You are responsible for closing the message box when the process is complete.
15387          * @param {String} msg The message box body text
15388          * @param {String} title (optional) The title bar text
15389          * @return {Roo.MessageBox} This message box
15390          */
15391         wait : function(msg, title){
15392             this.show({
15393                 title : title,
15394                 msg : msg,
15395                 buttons: false,
15396                 closable:false,
15397                 progress:true,
15398                 modal:true,
15399                 width:300,
15400                 wait:true
15401             });
15402             waitTimer = Roo.TaskMgr.start({
15403                 run: function(i){
15404                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15405                 },
15406                 interval: 1000
15407             });
15408             return this;
15409         },
15410
15411         /**
15412          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15413          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15414          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15415          * @param {String} title The title bar text
15416          * @param {String} msg The message box body text
15417          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15418          * @param {Object} scope (optional) The scope of the callback function
15419          * @return {Roo.MessageBox} This message box
15420          */
15421         confirm : function(title, msg, fn, scope){
15422             this.show({
15423                 title : title,
15424                 msg : msg,
15425                 buttons: this.YESNO,
15426                 fn: fn,
15427                 scope : scope,
15428                 modal : true
15429             });
15430             return this;
15431         },
15432
15433         /**
15434          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15435          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15436          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15437          * (could also be the top-right close button) and the text that was entered will be passed as the two
15438          * parameters to the callback.
15439          * @param {String} title The title bar text
15440          * @param {String} msg The message box body text
15441          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15442          * @param {Object} scope (optional) The scope of the callback function
15443          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15444          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15445          * @return {Roo.MessageBox} This message box
15446          */
15447         prompt : function(title, msg, fn, scope, multiline){
15448             this.show({
15449                 title : title,
15450                 msg : msg,
15451                 buttons: this.OKCANCEL,
15452                 fn: fn,
15453                 minWidth:250,
15454                 scope : scope,
15455                 prompt:true,
15456                 multiline: multiline,
15457                 modal : true
15458             });
15459             return this;
15460         },
15461
15462         /**
15463          * Button config that displays a single OK button
15464          * @type Object
15465          */
15466         OK : {ok:true},
15467         /**
15468          * Button config that displays Yes and No buttons
15469          * @type Object
15470          */
15471         YESNO : {yes:true, no:true},
15472         /**
15473          * Button config that displays OK and Cancel buttons
15474          * @type Object
15475          */
15476         OKCANCEL : {ok:true, cancel:true},
15477         /**
15478          * Button config that displays Yes, No and Cancel buttons
15479          * @type Object
15480          */
15481         YESNOCANCEL : {yes:true, no:true, cancel:true},
15482
15483         /**
15484          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15485          * @type Number
15486          */
15487         defaultTextHeight : 75,
15488         /**
15489          * The maximum width in pixels of the message box (defaults to 600)
15490          * @type Number
15491          */
15492         maxWidth : 600,
15493         /**
15494          * The minimum width in pixels of the message box (defaults to 100)
15495          * @type Number
15496          */
15497         minWidth : 100,
15498         /**
15499          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15500          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15501          * @type Number
15502          */
15503         minProgressWidth : 250,
15504         /**
15505          * An object containing the default button text strings that can be overriden for localized language support.
15506          * Supported properties are: ok, cancel, yes and no.
15507          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15508          * @type Object
15509          */
15510         buttonText : {
15511             ok : "OK",
15512             cancel : "Cancel",
15513             yes : "Yes",
15514             no : "No"
15515         }
15516     };
15517 }();
15518
15519 /**
15520  * Shorthand for {@link Roo.MessageBox}
15521  */
15522 Roo.Msg = Roo.MessageBox;/*
15523  * Based on:
15524  * Ext JS Library 1.1.1
15525  * Copyright(c) 2006-2007, Ext JS, LLC.
15526  *
15527  * Originally Released Under LGPL - original licence link has changed is not relivant.
15528  *
15529  * Fork - LGPL
15530  * <script type="text/javascript">
15531  */
15532 /**
15533  * @class Roo.QuickTips
15534  * Provides attractive and customizable tooltips for any element.
15535  * @singleton
15536  */
15537 Roo.QuickTips = function(){
15538     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15539     var ce, bd, xy, dd;
15540     var visible = false, disabled = true, inited = false;
15541     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15542     
15543     var onOver = function(e){
15544         if(disabled){
15545             return;
15546         }
15547         var t = e.getTarget();
15548         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15549             return;
15550         }
15551         if(ce && t == ce.el){
15552             clearTimeout(hideProc);
15553             return;
15554         }
15555         if(t && tagEls[t.id]){
15556             tagEls[t.id].el = t;
15557             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15558             return;
15559         }
15560         var ttp, et = Roo.fly(t);
15561         var ns = cfg.namespace;
15562         if(tm.interceptTitles && t.title){
15563             ttp = t.title;
15564             t.qtip = ttp;
15565             t.removeAttribute("title");
15566             e.preventDefault();
15567         }else{
15568             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15569         }
15570         if(ttp){
15571             showProc = show.defer(tm.showDelay, tm, [{
15572                 el: t, 
15573                 text: ttp, 
15574                 width: et.getAttributeNS(ns, cfg.width),
15575                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15576                 title: et.getAttributeNS(ns, cfg.title),
15577                     cls: et.getAttributeNS(ns, cfg.cls)
15578             }]);
15579         }
15580     };
15581     
15582     var onOut = function(e){
15583         clearTimeout(showProc);
15584         var t = e.getTarget();
15585         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15586             hideProc = setTimeout(hide, tm.hideDelay);
15587         }
15588     };
15589     
15590     var onMove = function(e){
15591         if(disabled){
15592             return;
15593         }
15594         xy = e.getXY();
15595         xy[1] += 18;
15596         if(tm.trackMouse && ce){
15597             el.setXY(xy);
15598         }
15599     };
15600     
15601     var onDown = function(e){
15602         clearTimeout(showProc);
15603         clearTimeout(hideProc);
15604         if(!e.within(el)){
15605             if(tm.hideOnClick){
15606                 hide();
15607                 tm.disable();
15608                 tm.enable.defer(100, tm);
15609             }
15610         }
15611     };
15612     
15613     var getPad = function(){
15614         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15615     };
15616
15617     var show = function(o){
15618         if(disabled){
15619             return;
15620         }
15621         clearTimeout(dismissProc);
15622         ce = o;
15623         if(removeCls){ // in case manually hidden
15624             el.removeClass(removeCls);
15625             removeCls = null;
15626         }
15627         if(ce.cls){
15628             el.addClass(ce.cls);
15629             removeCls = ce.cls;
15630         }
15631         if(ce.title){
15632             tipTitle.update(ce.title);
15633             tipTitle.show();
15634         }else{
15635             tipTitle.update('');
15636             tipTitle.hide();
15637         }
15638         el.dom.style.width  = tm.maxWidth+'px';
15639         //tipBody.dom.style.width = '';
15640         tipBodyText.update(o.text);
15641         var p = getPad(), w = ce.width;
15642         if(!w){
15643             var td = tipBodyText.dom;
15644             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15645             if(aw > tm.maxWidth){
15646                 w = tm.maxWidth;
15647             }else if(aw < tm.minWidth){
15648                 w = tm.minWidth;
15649             }else{
15650                 w = aw;
15651             }
15652         }
15653         //tipBody.setWidth(w);
15654         el.setWidth(parseInt(w, 10) + p);
15655         if(ce.autoHide === false){
15656             close.setDisplayed(true);
15657             if(dd){
15658                 dd.unlock();
15659             }
15660         }else{
15661             close.setDisplayed(false);
15662             if(dd){
15663                 dd.lock();
15664             }
15665         }
15666         if(xy){
15667             el.avoidY = xy[1]-18;
15668             el.setXY(xy);
15669         }
15670         if(tm.animate){
15671             el.setOpacity(.1);
15672             el.setStyle("visibility", "visible");
15673             el.fadeIn({callback: afterShow});
15674         }else{
15675             afterShow();
15676         }
15677     };
15678     
15679     var afterShow = function(){
15680         if(ce){
15681             el.show();
15682             esc.enable();
15683             if(tm.autoDismiss && ce.autoHide !== false){
15684                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15685             }
15686         }
15687     };
15688     
15689     var hide = function(noanim){
15690         clearTimeout(dismissProc);
15691         clearTimeout(hideProc);
15692         ce = null;
15693         if(el.isVisible()){
15694             esc.disable();
15695             if(noanim !== true && tm.animate){
15696                 el.fadeOut({callback: afterHide});
15697             }else{
15698                 afterHide();
15699             } 
15700         }
15701     };
15702     
15703     var afterHide = function(){
15704         el.hide();
15705         if(removeCls){
15706             el.removeClass(removeCls);
15707             removeCls = null;
15708         }
15709     };
15710     
15711     return {
15712         /**
15713         * @cfg {Number} minWidth
15714         * The minimum width of the quick tip (defaults to 40)
15715         */
15716        minWidth : 40,
15717         /**
15718         * @cfg {Number} maxWidth
15719         * The maximum width of the quick tip (defaults to 300)
15720         */
15721        maxWidth : 300,
15722         /**
15723         * @cfg {Boolean} interceptTitles
15724         * True to automatically use the element's DOM title value if available (defaults to false)
15725         */
15726        interceptTitles : false,
15727         /**
15728         * @cfg {Boolean} trackMouse
15729         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15730         */
15731        trackMouse : false,
15732         /**
15733         * @cfg {Boolean} hideOnClick
15734         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15735         */
15736        hideOnClick : true,
15737         /**
15738         * @cfg {Number} showDelay
15739         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15740         */
15741        showDelay : 500,
15742         /**
15743         * @cfg {Number} hideDelay
15744         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15745         */
15746        hideDelay : 200,
15747         /**
15748         * @cfg {Boolean} autoHide
15749         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15750         * Used in conjunction with hideDelay.
15751         */
15752        autoHide : true,
15753         /**
15754         * @cfg {Boolean}
15755         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15756         * (defaults to true).  Used in conjunction with autoDismissDelay.
15757         */
15758        autoDismiss : true,
15759         /**
15760         * @cfg {Number}
15761         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15762         */
15763        autoDismissDelay : 5000,
15764        /**
15765         * @cfg {Boolean} animate
15766         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15767         */
15768        animate : false,
15769
15770        /**
15771         * @cfg {String} title
15772         * Title text to display (defaults to '').  This can be any valid HTML markup.
15773         */
15774         title: '',
15775        /**
15776         * @cfg {String} text
15777         * Body text to display (defaults to '').  This can be any valid HTML markup.
15778         */
15779         text : '',
15780        /**
15781         * @cfg {String} cls
15782         * A CSS class to apply to the base quick tip element (defaults to '').
15783         */
15784         cls : '',
15785        /**
15786         * @cfg {Number} width
15787         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15788         * minWidth or maxWidth.
15789         */
15790         width : null,
15791
15792     /**
15793      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15794      * or display QuickTips in a page.
15795      */
15796        init : function(){
15797           tm = Roo.QuickTips;
15798           cfg = tm.tagConfig;
15799           if(!inited){
15800               if(!Roo.isReady){ // allow calling of init() before onReady
15801                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15802                   return;
15803               }
15804               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15805               el.fxDefaults = {stopFx: true};
15806               // maximum custom styling
15807               //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>');
15808               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>');              
15809               tipTitle = el.child('h3');
15810               tipTitle.enableDisplayMode("block");
15811               tipBody = el.child('div.x-tip-bd');
15812               tipBodyText = el.child('div.x-tip-bd-inner');
15813               //bdLeft = el.child('div.x-tip-bd-left');
15814               //bdRight = el.child('div.x-tip-bd-right');
15815               close = el.child('div.x-tip-close');
15816               close.enableDisplayMode("block");
15817               close.on("click", hide);
15818               var d = Roo.get(document);
15819               d.on("mousedown", onDown);
15820               d.on("mouseover", onOver);
15821               d.on("mouseout", onOut);
15822               d.on("mousemove", onMove);
15823               esc = d.addKeyListener(27, hide);
15824               esc.disable();
15825               if(Roo.dd.DD){
15826                   dd = el.initDD("default", null, {
15827                       onDrag : function(){
15828                           el.sync();  
15829                       }
15830                   });
15831                   dd.setHandleElId(tipTitle.id);
15832                   dd.lock();
15833               }
15834               inited = true;
15835           }
15836           this.enable(); 
15837        },
15838
15839     /**
15840      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15841      * are supported:
15842      * <pre>
15843 Property    Type                   Description
15844 ----------  ---------------------  ------------------------------------------------------------------------
15845 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15846      * </ul>
15847      * @param {Object} config The config object
15848      */
15849        register : function(config){
15850            var cs = config instanceof Array ? config : arguments;
15851            for(var i = 0, len = cs.length; i < len; i++) {
15852                var c = cs[i];
15853                var target = c.target;
15854                if(target){
15855                    if(target instanceof Array){
15856                        for(var j = 0, jlen = target.length; j < jlen; j++){
15857                            tagEls[target[j]] = c;
15858                        }
15859                    }else{
15860                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15861                    }
15862                }
15863            }
15864        },
15865
15866     /**
15867      * Removes this quick tip from its element and destroys it.
15868      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15869      */
15870        unregister : function(el){
15871            delete tagEls[Roo.id(el)];
15872        },
15873
15874     /**
15875      * Enable this quick tip.
15876      */
15877        enable : function(){
15878            if(inited && disabled){
15879                locks.pop();
15880                if(locks.length < 1){
15881                    disabled = false;
15882                }
15883            }
15884        },
15885
15886     /**
15887      * Disable this quick tip.
15888      */
15889        disable : function(){
15890           disabled = true;
15891           clearTimeout(showProc);
15892           clearTimeout(hideProc);
15893           clearTimeout(dismissProc);
15894           if(ce){
15895               hide(true);
15896           }
15897           locks.push(1);
15898        },
15899
15900     /**
15901      * Returns true if the quick tip is enabled, else false.
15902      */
15903        isEnabled : function(){
15904             return !disabled;
15905        },
15906
15907         // private
15908        tagConfig : {
15909            namespace : "ext",
15910            attribute : "qtip",
15911            width : "width",
15912            target : "target",
15913            title : "qtitle",
15914            hide : "hide",
15915            cls : "qclass"
15916        }
15917    };
15918 }();
15919
15920 // backwards compat
15921 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15922  * Based on:
15923  * Ext JS Library 1.1.1
15924  * Copyright(c) 2006-2007, Ext JS, LLC.
15925  *
15926  * Originally Released Under LGPL - original licence link has changed is not relivant.
15927  *
15928  * Fork - LGPL
15929  * <script type="text/javascript">
15930  */
15931  
15932
15933 /**
15934  * @class Roo.tree.TreePanel
15935  * @extends Roo.data.Tree
15936
15937  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15938  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15939  * @cfg {Boolean} enableDD true to enable drag and drop
15940  * @cfg {Boolean} enableDrag true to enable just drag
15941  * @cfg {Boolean} enableDrop true to enable just drop
15942  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15943  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15944  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15945  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15946  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15947  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15948  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15949  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15950  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15951  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15952  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15953  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15954  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15955  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15956  * @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>
15957  * @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>
15958  * 
15959  * @constructor
15960  * @param {String/HTMLElement/Element} el The container element
15961  * @param {Object} config
15962  */
15963 Roo.tree.TreePanel = function(el, config){
15964     var root = false;
15965     var loader = false;
15966     if (config.root) {
15967         root = config.root;
15968         delete config.root;
15969     }
15970     if (config.loader) {
15971         loader = config.loader;
15972         delete config.loader;
15973     }
15974     
15975     Roo.apply(this, config);
15976     Roo.tree.TreePanel.superclass.constructor.call(this);
15977     this.el = Roo.get(el);
15978     this.el.addClass('x-tree');
15979     //console.log(root);
15980     if (root) {
15981         this.setRootNode( Roo.factory(root, Roo.tree));
15982     }
15983     if (loader) {
15984         this.loader = Roo.factory(loader, Roo.tree);
15985     }
15986    /**
15987     * Read-only. The id of the container element becomes this TreePanel's id.
15988     */
15989     this.id = this.el.id;
15990     this.addEvents({
15991         /**
15992         * @event beforeload
15993         * Fires before a node is loaded, return false to cancel
15994         * @param {Node} node The node being loaded
15995         */
15996         "beforeload" : true,
15997         /**
15998         * @event load
15999         * Fires when a node is loaded
16000         * @param {Node} node The node that was loaded
16001         */
16002         "load" : true,
16003         /**
16004         * @event textchange
16005         * Fires when the text for a node is changed
16006         * @param {Node} node The node
16007         * @param {String} text The new text
16008         * @param {String} oldText The old text
16009         */
16010         "textchange" : true,
16011         /**
16012         * @event beforeexpand
16013         * Fires before a node is expanded, return false to cancel.
16014         * @param {Node} node The node
16015         * @param {Boolean} deep
16016         * @param {Boolean} anim
16017         */
16018         "beforeexpand" : true,
16019         /**
16020         * @event beforecollapse
16021         * Fires before a node is collapsed, return false to cancel.
16022         * @param {Node} node The node
16023         * @param {Boolean} deep
16024         * @param {Boolean} anim
16025         */
16026         "beforecollapse" : true,
16027         /**
16028         * @event expand
16029         * Fires when a node is expanded
16030         * @param {Node} node The node
16031         */
16032         "expand" : true,
16033         /**
16034         * @event disabledchange
16035         * Fires when the disabled status of a node changes
16036         * @param {Node} node The node
16037         * @param {Boolean} disabled
16038         */
16039         "disabledchange" : true,
16040         /**
16041         * @event collapse
16042         * Fires when a node is collapsed
16043         * @param {Node} node The node
16044         */
16045         "collapse" : true,
16046         /**
16047         * @event beforeclick
16048         * Fires before click processing on a node. Return false to cancel the default action.
16049         * @param {Node} node The node
16050         * @param {Roo.EventObject} e The event object
16051         */
16052         "beforeclick":true,
16053         /**
16054         * @event checkchange
16055         * Fires when a node with a checkbox's checked property changes
16056         * @param {Node} this This node
16057         * @param {Boolean} checked
16058         */
16059         "checkchange":true,
16060         /**
16061         * @event click
16062         * Fires when a node is clicked
16063         * @param {Node} node The node
16064         * @param {Roo.EventObject} e The event object
16065         */
16066         "click":true,
16067         /**
16068         * @event dblclick
16069         * Fires when a node is double clicked
16070         * @param {Node} node The node
16071         * @param {Roo.EventObject} e The event object
16072         */
16073         "dblclick":true,
16074         /**
16075         * @event contextmenu
16076         * Fires when a node is right clicked
16077         * @param {Node} node The node
16078         * @param {Roo.EventObject} e The event object
16079         */
16080         "contextmenu":true,
16081         /**
16082         * @event beforechildrenrendered
16083         * Fires right before the child nodes for a node are rendered
16084         * @param {Node} node The node
16085         */
16086         "beforechildrenrendered":true,
16087         /**
16088         * @event startdrag
16089         * Fires when a node starts being dragged
16090         * @param {Roo.tree.TreePanel} this
16091         * @param {Roo.tree.TreeNode} node
16092         * @param {event} e The raw browser event
16093         */ 
16094        "startdrag" : true,
16095        /**
16096         * @event enddrag
16097         * Fires when a drag operation is complete
16098         * @param {Roo.tree.TreePanel} this
16099         * @param {Roo.tree.TreeNode} node
16100         * @param {event} e The raw browser event
16101         */
16102        "enddrag" : true,
16103        /**
16104         * @event dragdrop
16105         * Fires when a dragged node is dropped on a valid DD target
16106         * @param {Roo.tree.TreePanel} this
16107         * @param {Roo.tree.TreeNode} node
16108         * @param {DD} dd The dd it was dropped on
16109         * @param {event} e The raw browser event
16110         */
16111        "dragdrop" : true,
16112        /**
16113         * @event beforenodedrop
16114         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16115         * passed to handlers has the following properties:<br />
16116         * <ul style="padding:5px;padding-left:16px;">
16117         * <li>tree - The TreePanel</li>
16118         * <li>target - The node being targeted for the drop</li>
16119         * <li>data - The drag data from the drag source</li>
16120         * <li>point - The point of the drop - append, above or below</li>
16121         * <li>source - The drag source</li>
16122         * <li>rawEvent - Raw mouse event</li>
16123         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16124         * to be inserted by setting them on this object.</li>
16125         * <li>cancel - Set this to true to cancel the drop.</li>
16126         * </ul>
16127         * @param {Object} dropEvent
16128         */
16129        "beforenodedrop" : true,
16130        /**
16131         * @event nodedrop
16132         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16133         * passed to handlers has the following properties:<br />
16134         * <ul style="padding:5px;padding-left:16px;">
16135         * <li>tree - The TreePanel</li>
16136         * <li>target - The node being targeted for the drop</li>
16137         * <li>data - The drag data from the drag source</li>
16138         * <li>point - The point of the drop - append, above or below</li>
16139         * <li>source - The drag source</li>
16140         * <li>rawEvent - Raw mouse event</li>
16141         * <li>dropNode - Dropped node(s).</li>
16142         * </ul>
16143         * @param {Object} dropEvent
16144         */
16145        "nodedrop" : true,
16146         /**
16147         * @event nodedragover
16148         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16149         * passed to handlers has the following properties:<br />
16150         * <ul style="padding:5px;padding-left:16px;">
16151         * <li>tree - The TreePanel</li>
16152         * <li>target - The node being targeted for the drop</li>
16153         * <li>data - The drag data from the drag source</li>
16154         * <li>point - The point of the drop - append, above or below</li>
16155         * <li>source - The drag source</li>
16156         * <li>rawEvent - Raw mouse event</li>
16157         * <li>dropNode - Drop node(s) provided by the source.</li>
16158         * <li>cancel - Set this to true to signal drop not allowed.</li>
16159         * </ul>
16160         * @param {Object} dragOverEvent
16161         */
16162        "nodedragover" : true
16163         
16164     });
16165     if(this.singleExpand){
16166        this.on("beforeexpand", this.restrictExpand, this);
16167     }
16168     if (this.editor) {
16169         this.editor.tree = this;
16170         this.editor = Roo.factory(this.editor, Roo.tree);
16171     }
16172     
16173     if (this.selModel) {
16174         this.selModel = Roo.factory(this.selModel, Roo.tree);
16175     }
16176    
16177 };
16178 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16179     rootVisible : true,
16180     animate: Roo.enableFx,
16181     lines : true,
16182     enableDD : false,
16183     hlDrop : Roo.enableFx,
16184   
16185     renderer: false,
16186     
16187     rendererTip: false,
16188     // private
16189     restrictExpand : function(node){
16190         var p = node.parentNode;
16191         if(p){
16192             if(p.expandedChild && p.expandedChild.parentNode == p){
16193                 p.expandedChild.collapse();
16194             }
16195             p.expandedChild = node;
16196         }
16197     },
16198
16199     // private override
16200     setRootNode : function(node){
16201         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16202         if(!this.rootVisible){
16203             node.ui = new Roo.tree.RootTreeNodeUI(node);
16204         }
16205         return node;
16206     },
16207
16208     /**
16209      * Returns the container element for this TreePanel
16210      */
16211     getEl : function(){
16212         return this.el;
16213     },
16214
16215     /**
16216      * Returns the default TreeLoader for this TreePanel
16217      */
16218     getLoader : function(){
16219         return this.loader;
16220     },
16221
16222     /**
16223      * Expand all nodes
16224      */
16225     expandAll : function(){
16226         this.root.expand(true);
16227     },
16228
16229     /**
16230      * Collapse all nodes
16231      */
16232     collapseAll : function(){
16233         this.root.collapse(true);
16234     },
16235
16236     /**
16237      * Returns the selection model used by this TreePanel
16238      */
16239     getSelectionModel : function(){
16240         if(!this.selModel){
16241             this.selModel = new Roo.tree.DefaultSelectionModel();
16242         }
16243         return this.selModel;
16244     },
16245
16246     /**
16247      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16248      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16249      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16250      * @return {Array}
16251      */
16252     getChecked : function(a, startNode){
16253         startNode = startNode || this.root;
16254         var r = [];
16255         var f = function(){
16256             if(this.attributes.checked){
16257                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16258             }
16259         }
16260         startNode.cascade(f);
16261         return r;
16262     },
16263
16264     /**
16265      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16266      * @param {String} path
16267      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16268      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16269      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16270      */
16271     expandPath : function(path, attr, callback){
16272         attr = attr || "id";
16273         var keys = path.split(this.pathSeparator);
16274         var curNode = this.root;
16275         if(curNode.attributes[attr] != keys[1]){ // invalid root
16276             if(callback){
16277                 callback(false, null);
16278             }
16279             return;
16280         }
16281         var index = 1;
16282         var f = function(){
16283             if(++index == keys.length){
16284                 if(callback){
16285                     callback(true, curNode);
16286                 }
16287                 return;
16288             }
16289             var c = curNode.findChild(attr, keys[index]);
16290             if(!c){
16291                 if(callback){
16292                     callback(false, curNode);
16293                 }
16294                 return;
16295             }
16296             curNode = c;
16297             c.expand(false, false, f);
16298         };
16299         curNode.expand(false, false, f);
16300     },
16301
16302     /**
16303      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16304      * @param {String} path
16305      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16306      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16307      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16308      */
16309     selectPath : function(path, attr, callback){
16310         attr = attr || "id";
16311         var keys = path.split(this.pathSeparator);
16312         var v = keys.pop();
16313         if(keys.length > 0){
16314             var f = function(success, node){
16315                 if(success && node){
16316                     var n = node.findChild(attr, v);
16317                     if(n){
16318                         n.select();
16319                         if(callback){
16320                             callback(true, n);
16321                         }
16322                     }else if(callback){
16323                         callback(false, n);
16324                     }
16325                 }else{
16326                     if(callback){
16327                         callback(false, n);
16328                     }
16329                 }
16330             };
16331             this.expandPath(keys.join(this.pathSeparator), attr, f);
16332         }else{
16333             this.root.select();
16334             if(callback){
16335                 callback(true, this.root);
16336             }
16337         }
16338     },
16339
16340     getTreeEl : function(){
16341         return this.el;
16342     },
16343
16344     /**
16345      * Trigger rendering of this TreePanel
16346      */
16347     render : function(){
16348         if (this.innerCt) {
16349             return this; // stop it rendering more than once!!
16350         }
16351         
16352         this.innerCt = this.el.createChild({tag:"ul",
16353                cls:"x-tree-root-ct " +
16354                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16355
16356         if(this.containerScroll){
16357             Roo.dd.ScrollManager.register(this.el);
16358         }
16359         if((this.enableDD || this.enableDrop) && !this.dropZone){
16360            /**
16361             * The dropZone used by this tree if drop is enabled
16362             * @type Roo.tree.TreeDropZone
16363             */
16364              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16365                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16366            });
16367         }
16368         if((this.enableDD || this.enableDrag) && !this.dragZone){
16369            /**
16370             * The dragZone used by this tree if drag is enabled
16371             * @type Roo.tree.TreeDragZone
16372             */
16373             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16374                ddGroup: this.ddGroup || "TreeDD",
16375                scroll: this.ddScroll
16376            });
16377         }
16378         this.getSelectionModel().init(this);
16379         if (!this.root) {
16380             Roo.log("ROOT not set in tree");
16381             return this;
16382         }
16383         this.root.render();
16384         if(!this.rootVisible){
16385             this.root.renderChildren();
16386         }
16387         return this;
16388     }
16389 });/*
16390  * Based on:
16391  * Ext JS Library 1.1.1
16392  * Copyright(c) 2006-2007, Ext JS, LLC.
16393  *
16394  * Originally Released Under LGPL - original licence link has changed is not relivant.
16395  *
16396  * Fork - LGPL
16397  * <script type="text/javascript">
16398  */
16399  
16400
16401 /**
16402  * @class Roo.tree.DefaultSelectionModel
16403  * @extends Roo.util.Observable
16404  * The default single selection for a TreePanel.
16405  * @param {Object} cfg Configuration
16406  */
16407 Roo.tree.DefaultSelectionModel = function(cfg){
16408    this.selNode = null;
16409    
16410    
16411    
16412    this.addEvents({
16413        /**
16414         * @event selectionchange
16415         * Fires when the selected node changes
16416         * @param {DefaultSelectionModel} this
16417         * @param {TreeNode} node the new selection
16418         */
16419        "selectionchange" : true,
16420
16421        /**
16422         * @event beforeselect
16423         * Fires before the selected node changes, return false to cancel the change
16424         * @param {DefaultSelectionModel} this
16425         * @param {TreeNode} node the new selection
16426         * @param {TreeNode} node the old selection
16427         */
16428        "beforeselect" : true
16429    });
16430    
16431     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16432 };
16433
16434 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16435     init : function(tree){
16436         this.tree = tree;
16437         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16438         tree.on("click", this.onNodeClick, this);
16439     },
16440     
16441     onNodeClick : function(node, e){
16442         if (e.ctrlKey && this.selNode == node)  {
16443             this.unselect(node);
16444             return;
16445         }
16446         this.select(node);
16447     },
16448     
16449     /**
16450      * Select a node.
16451      * @param {TreeNode} node The node to select
16452      * @return {TreeNode} The selected node
16453      */
16454     select : function(node){
16455         var last = this.selNode;
16456         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16457             if(last){
16458                 last.ui.onSelectedChange(false);
16459             }
16460             this.selNode = node;
16461             node.ui.onSelectedChange(true);
16462             this.fireEvent("selectionchange", this, node, last);
16463         }
16464         return node;
16465     },
16466     
16467     /**
16468      * Deselect a node.
16469      * @param {TreeNode} node The node to unselect
16470      */
16471     unselect : function(node){
16472         if(this.selNode == node){
16473             this.clearSelections();
16474         }    
16475     },
16476     
16477     /**
16478      * Clear all selections
16479      */
16480     clearSelections : function(){
16481         var n = this.selNode;
16482         if(n){
16483             n.ui.onSelectedChange(false);
16484             this.selNode = null;
16485             this.fireEvent("selectionchange", this, null);
16486         }
16487         return n;
16488     },
16489     
16490     /**
16491      * Get the selected node
16492      * @return {TreeNode} The selected node
16493      */
16494     getSelectedNode : function(){
16495         return this.selNode;    
16496     },
16497     
16498     /**
16499      * Returns true if the node is selected
16500      * @param {TreeNode} node The node to check
16501      * @return {Boolean}
16502      */
16503     isSelected : function(node){
16504         return this.selNode == node;  
16505     },
16506
16507     /**
16508      * Selects the node above the selected node in the tree, intelligently walking the nodes
16509      * @return TreeNode The new selection
16510      */
16511     selectPrevious : function(){
16512         var s = this.selNode || this.lastSelNode;
16513         if(!s){
16514             return null;
16515         }
16516         var ps = s.previousSibling;
16517         if(ps){
16518             if(!ps.isExpanded() || ps.childNodes.length < 1){
16519                 return this.select(ps);
16520             } else{
16521                 var lc = ps.lastChild;
16522                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16523                     lc = lc.lastChild;
16524                 }
16525                 return this.select(lc);
16526             }
16527         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16528             return this.select(s.parentNode);
16529         }
16530         return null;
16531     },
16532
16533     /**
16534      * Selects the node above the selected node in the tree, intelligently walking the nodes
16535      * @return TreeNode The new selection
16536      */
16537     selectNext : function(){
16538         var s = this.selNode || this.lastSelNode;
16539         if(!s){
16540             return null;
16541         }
16542         if(s.firstChild && s.isExpanded()){
16543              return this.select(s.firstChild);
16544          }else if(s.nextSibling){
16545              return this.select(s.nextSibling);
16546          }else if(s.parentNode){
16547             var newS = null;
16548             s.parentNode.bubble(function(){
16549                 if(this.nextSibling){
16550                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16551                     return false;
16552                 }
16553             });
16554             return newS;
16555          }
16556         return null;
16557     },
16558
16559     onKeyDown : function(e){
16560         var s = this.selNode || this.lastSelNode;
16561         // undesirable, but required
16562         var sm = this;
16563         if(!s){
16564             return;
16565         }
16566         var k = e.getKey();
16567         switch(k){
16568              case e.DOWN:
16569                  e.stopEvent();
16570                  this.selectNext();
16571              break;
16572              case e.UP:
16573                  e.stopEvent();
16574                  this.selectPrevious();
16575              break;
16576              case e.RIGHT:
16577                  e.preventDefault();
16578                  if(s.hasChildNodes()){
16579                      if(!s.isExpanded()){
16580                          s.expand();
16581                      }else if(s.firstChild){
16582                          this.select(s.firstChild, e);
16583                      }
16584                  }
16585              break;
16586              case e.LEFT:
16587                  e.preventDefault();
16588                  if(s.hasChildNodes() && s.isExpanded()){
16589                      s.collapse();
16590                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16591                      this.select(s.parentNode, e);
16592                  }
16593              break;
16594         };
16595     }
16596 });
16597
16598 /**
16599  * @class Roo.tree.MultiSelectionModel
16600  * @extends Roo.util.Observable
16601  * Multi selection for a TreePanel.
16602  * @param {Object} cfg Configuration
16603  */
16604 Roo.tree.MultiSelectionModel = function(){
16605    this.selNodes = [];
16606    this.selMap = {};
16607    this.addEvents({
16608        /**
16609         * @event selectionchange
16610         * Fires when the selected nodes change
16611         * @param {MultiSelectionModel} this
16612         * @param {Array} nodes Array of the selected nodes
16613         */
16614        "selectionchange" : true
16615    });
16616    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16617    
16618 };
16619
16620 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16621     init : function(tree){
16622         this.tree = tree;
16623         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16624         tree.on("click", this.onNodeClick, this);
16625     },
16626     
16627     onNodeClick : function(node, e){
16628         this.select(node, e, e.ctrlKey);
16629     },
16630     
16631     /**
16632      * Select a node.
16633      * @param {TreeNode} node The node to select
16634      * @param {EventObject} e (optional) An event associated with the selection
16635      * @param {Boolean} keepExisting True to retain existing selections
16636      * @return {TreeNode} The selected node
16637      */
16638     select : function(node, e, keepExisting){
16639         if(keepExisting !== true){
16640             this.clearSelections(true);
16641         }
16642         if(this.isSelected(node)){
16643             this.lastSelNode = node;
16644             return node;
16645         }
16646         this.selNodes.push(node);
16647         this.selMap[node.id] = node;
16648         this.lastSelNode = node;
16649         node.ui.onSelectedChange(true);
16650         this.fireEvent("selectionchange", this, this.selNodes);
16651         return node;
16652     },
16653     
16654     /**
16655      * Deselect a node.
16656      * @param {TreeNode} node The node to unselect
16657      */
16658     unselect : function(node){
16659         if(this.selMap[node.id]){
16660             node.ui.onSelectedChange(false);
16661             var sn = this.selNodes;
16662             var index = -1;
16663             if(sn.indexOf){
16664                 index = sn.indexOf(node);
16665             }else{
16666                 for(var i = 0, len = sn.length; i < len; i++){
16667                     if(sn[i] == node){
16668                         index = i;
16669                         break;
16670                     }
16671                 }
16672             }
16673             if(index != -1){
16674                 this.selNodes.splice(index, 1);
16675             }
16676             delete this.selMap[node.id];
16677             this.fireEvent("selectionchange", this, this.selNodes);
16678         }
16679     },
16680     
16681     /**
16682      * Clear all selections
16683      */
16684     clearSelections : function(suppressEvent){
16685         var sn = this.selNodes;
16686         if(sn.length > 0){
16687             for(var i = 0, len = sn.length; i < len; i++){
16688                 sn[i].ui.onSelectedChange(false);
16689             }
16690             this.selNodes = [];
16691             this.selMap = {};
16692             if(suppressEvent !== true){
16693                 this.fireEvent("selectionchange", this, this.selNodes);
16694             }
16695         }
16696     },
16697     
16698     /**
16699      * Returns true if the node is selected
16700      * @param {TreeNode} node The node to check
16701      * @return {Boolean}
16702      */
16703     isSelected : function(node){
16704         return this.selMap[node.id] ? true : false;  
16705     },
16706     
16707     /**
16708      * Returns an array of the selected nodes
16709      * @return {Array}
16710      */
16711     getSelectedNodes : function(){
16712         return this.selNodes;    
16713     },
16714
16715     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16716
16717     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16718
16719     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16720 });/*
16721  * Based on:
16722  * Ext JS Library 1.1.1
16723  * Copyright(c) 2006-2007, Ext JS, LLC.
16724  *
16725  * Originally Released Under LGPL - original licence link has changed is not relivant.
16726  *
16727  * Fork - LGPL
16728  * <script type="text/javascript">
16729  */
16730  
16731 /**
16732  * @class Roo.tree.TreeNode
16733  * @extends Roo.data.Node
16734  * @cfg {String} text The text for this node
16735  * @cfg {Boolean} expanded true to start the node expanded
16736  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16737  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16738  * @cfg {Boolean} disabled true to start the node disabled
16739  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16740  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16741  * @cfg {String} cls A css class to be added to the node
16742  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16743  * @cfg {String} href URL of the link used for the node (defaults to #)
16744  * @cfg {String} hrefTarget target frame for the link
16745  * @cfg {String} qtip An Ext QuickTip for the node
16746  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16747  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16748  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16749  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16750  * (defaults to undefined with no checkbox rendered)
16751  * @constructor
16752  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16753  */
16754 Roo.tree.TreeNode = function(attributes){
16755     attributes = attributes || {};
16756     if(typeof attributes == "string"){
16757         attributes = {text: attributes};
16758     }
16759     this.childrenRendered = false;
16760     this.rendered = false;
16761     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16762     this.expanded = attributes.expanded === true;
16763     this.isTarget = attributes.isTarget !== false;
16764     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16765     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16766
16767     /**
16768      * Read-only. The text for this node. To change it use setText().
16769      * @type String
16770      */
16771     this.text = attributes.text;
16772     /**
16773      * True if this node is disabled.
16774      * @type Boolean
16775      */
16776     this.disabled = attributes.disabled === true;
16777
16778     this.addEvents({
16779         /**
16780         * @event textchange
16781         * Fires when the text for this node is changed
16782         * @param {Node} this This node
16783         * @param {String} text The new text
16784         * @param {String} oldText The old text
16785         */
16786         "textchange" : true,
16787         /**
16788         * @event beforeexpand
16789         * Fires before this node is expanded, return false to cancel.
16790         * @param {Node} this This node
16791         * @param {Boolean} deep
16792         * @param {Boolean} anim
16793         */
16794         "beforeexpand" : true,
16795         /**
16796         * @event beforecollapse
16797         * Fires before this node is collapsed, return false to cancel.
16798         * @param {Node} this This node
16799         * @param {Boolean} deep
16800         * @param {Boolean} anim
16801         */
16802         "beforecollapse" : true,
16803         /**
16804         * @event expand
16805         * Fires when this node is expanded
16806         * @param {Node} this This node
16807         */
16808         "expand" : true,
16809         /**
16810         * @event disabledchange
16811         * Fires when the disabled status of this node changes
16812         * @param {Node} this This node
16813         * @param {Boolean} disabled
16814         */
16815         "disabledchange" : true,
16816         /**
16817         * @event collapse
16818         * Fires when this node is collapsed
16819         * @param {Node} this This node
16820         */
16821         "collapse" : true,
16822         /**
16823         * @event beforeclick
16824         * Fires before click processing. Return false to cancel the default action.
16825         * @param {Node} this This node
16826         * @param {Roo.EventObject} e The event object
16827         */
16828         "beforeclick":true,
16829         /**
16830         * @event checkchange
16831         * Fires when a node with a checkbox's checked property changes
16832         * @param {Node} this This node
16833         * @param {Boolean} checked
16834         */
16835         "checkchange":true,
16836         /**
16837         * @event click
16838         * Fires when this node is clicked
16839         * @param {Node} this This node
16840         * @param {Roo.EventObject} e The event object
16841         */
16842         "click":true,
16843         /**
16844         * @event dblclick
16845         * Fires when this node is double clicked
16846         * @param {Node} this This node
16847         * @param {Roo.EventObject} e The event object
16848         */
16849         "dblclick":true,
16850         /**
16851         * @event contextmenu
16852         * Fires when this node is right clicked
16853         * @param {Node} this This node
16854         * @param {Roo.EventObject} e The event object
16855         */
16856         "contextmenu":true,
16857         /**
16858         * @event beforechildrenrendered
16859         * Fires right before the child nodes for this node are rendered
16860         * @param {Node} this This node
16861         */
16862         "beforechildrenrendered":true
16863     });
16864
16865     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16866
16867     /**
16868      * Read-only. The UI for this node
16869      * @type TreeNodeUI
16870      */
16871     this.ui = new uiClass(this);
16872     
16873     // finally support items[]
16874     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16875         return;
16876     }
16877     
16878     
16879     Roo.each(this.attributes.items, function(c) {
16880         this.appendChild(Roo.factory(c,Roo.Tree));
16881     }, this);
16882     delete this.attributes.items;
16883     
16884     
16885     
16886 };
16887 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16888     preventHScroll: true,
16889     /**
16890      * Returns true if this node is expanded
16891      * @return {Boolean}
16892      */
16893     isExpanded : function(){
16894         return this.expanded;
16895     },
16896
16897     /**
16898      * Returns the UI object for this node
16899      * @return {TreeNodeUI}
16900      */
16901     getUI : function(){
16902         return this.ui;
16903     },
16904
16905     // private override
16906     setFirstChild : function(node){
16907         var of = this.firstChild;
16908         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16909         if(this.childrenRendered && of && node != of){
16910             of.renderIndent(true, true);
16911         }
16912         if(this.rendered){
16913             this.renderIndent(true, true);
16914         }
16915     },
16916
16917     // private override
16918     setLastChild : function(node){
16919         var ol = this.lastChild;
16920         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16921         if(this.childrenRendered && ol && node != ol){
16922             ol.renderIndent(true, true);
16923         }
16924         if(this.rendered){
16925             this.renderIndent(true, true);
16926         }
16927     },
16928
16929     // these methods are overridden to provide lazy rendering support
16930     // private override
16931     appendChild : function()
16932     {
16933         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16934         if(node && this.childrenRendered){
16935             node.render();
16936         }
16937         this.ui.updateExpandIcon();
16938         return node;
16939     },
16940
16941     // private override
16942     removeChild : function(node){
16943         this.ownerTree.getSelectionModel().unselect(node);
16944         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16945         // if it's been rendered remove dom node
16946         if(this.childrenRendered){
16947             node.ui.remove();
16948         }
16949         if(this.childNodes.length < 1){
16950             this.collapse(false, false);
16951         }else{
16952             this.ui.updateExpandIcon();
16953         }
16954         if(!this.firstChild) {
16955             this.childrenRendered = false;
16956         }
16957         return node;
16958     },
16959
16960     // private override
16961     insertBefore : function(node, refNode){
16962         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16963         if(newNode && refNode && this.childrenRendered){
16964             node.render();
16965         }
16966         this.ui.updateExpandIcon();
16967         return newNode;
16968     },
16969
16970     /**
16971      * Sets the text for this node
16972      * @param {String} text
16973      */
16974     setText : function(text){
16975         var oldText = this.text;
16976         this.text = text;
16977         this.attributes.text = text;
16978         if(this.rendered){ // event without subscribing
16979             this.ui.onTextChange(this, text, oldText);
16980         }
16981         this.fireEvent("textchange", this, text, oldText);
16982     },
16983
16984     /**
16985      * Triggers selection of this node
16986      */
16987     select : function(){
16988         this.getOwnerTree().getSelectionModel().select(this);
16989     },
16990
16991     /**
16992      * Triggers deselection of this node
16993      */
16994     unselect : function(){
16995         this.getOwnerTree().getSelectionModel().unselect(this);
16996     },
16997
16998     /**
16999      * Returns true if this node is selected
17000      * @return {Boolean}
17001      */
17002     isSelected : function(){
17003         return this.getOwnerTree().getSelectionModel().isSelected(this);
17004     },
17005
17006     /**
17007      * Expand this node.
17008      * @param {Boolean} deep (optional) True to expand all children as well
17009      * @param {Boolean} anim (optional) false to cancel the default animation
17010      * @param {Function} callback (optional) A callback to be called when
17011      * expanding this node completes (does not wait for deep expand to complete).
17012      * Called with 1 parameter, this node.
17013      */
17014     expand : function(deep, anim, callback){
17015         if(!this.expanded){
17016             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17017                 return;
17018             }
17019             if(!this.childrenRendered){
17020                 this.renderChildren();
17021             }
17022             this.expanded = true;
17023             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17024                 this.ui.animExpand(function(){
17025                     this.fireEvent("expand", this);
17026                     if(typeof callback == "function"){
17027                         callback(this);
17028                     }
17029                     if(deep === true){
17030                         this.expandChildNodes(true);
17031                     }
17032                 }.createDelegate(this));
17033                 return;
17034             }else{
17035                 this.ui.expand();
17036                 this.fireEvent("expand", this);
17037                 if(typeof callback == "function"){
17038                     callback(this);
17039                 }
17040             }
17041         }else{
17042            if(typeof callback == "function"){
17043                callback(this);
17044            }
17045         }
17046         if(deep === true){
17047             this.expandChildNodes(true);
17048         }
17049     },
17050
17051     isHiddenRoot : function(){
17052         return this.isRoot && !this.getOwnerTree().rootVisible;
17053     },
17054
17055     /**
17056      * Collapse this node.
17057      * @param {Boolean} deep (optional) True to collapse all children as well
17058      * @param {Boolean} anim (optional) false to cancel the default animation
17059      */
17060     collapse : function(deep, anim){
17061         if(this.expanded && !this.isHiddenRoot()){
17062             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17063                 return;
17064             }
17065             this.expanded = false;
17066             if((this.getOwnerTree().animate && anim !== false) || anim){
17067                 this.ui.animCollapse(function(){
17068                     this.fireEvent("collapse", this);
17069                     if(deep === true){
17070                         this.collapseChildNodes(true);
17071                     }
17072                 }.createDelegate(this));
17073                 return;
17074             }else{
17075                 this.ui.collapse();
17076                 this.fireEvent("collapse", this);
17077             }
17078         }
17079         if(deep === true){
17080             var cs = this.childNodes;
17081             for(var i = 0, len = cs.length; i < len; i++) {
17082                 cs[i].collapse(true, false);
17083             }
17084         }
17085     },
17086
17087     // private
17088     delayedExpand : function(delay){
17089         if(!this.expandProcId){
17090             this.expandProcId = this.expand.defer(delay, this);
17091         }
17092     },
17093
17094     // private
17095     cancelExpand : function(){
17096         if(this.expandProcId){
17097             clearTimeout(this.expandProcId);
17098         }
17099         this.expandProcId = false;
17100     },
17101
17102     /**
17103      * Toggles expanded/collapsed state of the node
17104      */
17105     toggle : function(){
17106         if(this.expanded){
17107             this.collapse();
17108         }else{
17109             this.expand();
17110         }
17111     },
17112
17113     /**
17114      * Ensures all parent nodes are expanded
17115      */
17116     ensureVisible : function(callback){
17117         var tree = this.getOwnerTree();
17118         tree.expandPath(this.parentNode.getPath(), false, function(){
17119             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17120             Roo.callback(callback);
17121         }.createDelegate(this));
17122     },
17123
17124     /**
17125      * Expand all child nodes
17126      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17127      */
17128     expandChildNodes : function(deep){
17129         var cs = this.childNodes;
17130         for(var i = 0, len = cs.length; i < len; i++) {
17131                 cs[i].expand(deep);
17132         }
17133     },
17134
17135     /**
17136      * Collapse all child nodes
17137      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17138      */
17139     collapseChildNodes : function(deep){
17140         var cs = this.childNodes;
17141         for(var i = 0, len = cs.length; i < len; i++) {
17142                 cs[i].collapse(deep);
17143         }
17144     },
17145
17146     /**
17147      * Disables this node
17148      */
17149     disable : function(){
17150         this.disabled = true;
17151         this.unselect();
17152         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17153             this.ui.onDisableChange(this, true);
17154         }
17155         this.fireEvent("disabledchange", this, true);
17156     },
17157
17158     /**
17159      * Enables this node
17160      */
17161     enable : function(){
17162         this.disabled = false;
17163         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17164             this.ui.onDisableChange(this, false);
17165         }
17166         this.fireEvent("disabledchange", this, false);
17167     },
17168
17169     // private
17170     renderChildren : function(suppressEvent){
17171         if(suppressEvent !== false){
17172             this.fireEvent("beforechildrenrendered", this);
17173         }
17174         var cs = this.childNodes;
17175         for(var i = 0, len = cs.length; i < len; i++){
17176             cs[i].render(true);
17177         }
17178         this.childrenRendered = true;
17179     },
17180
17181     // private
17182     sort : function(fn, scope){
17183         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17184         if(this.childrenRendered){
17185             var cs = this.childNodes;
17186             for(var i = 0, len = cs.length; i < len; i++){
17187                 cs[i].render(true);
17188             }
17189         }
17190     },
17191
17192     // private
17193     render : function(bulkRender){
17194         this.ui.render(bulkRender);
17195         if(!this.rendered){
17196             this.rendered = true;
17197             if(this.expanded){
17198                 this.expanded = false;
17199                 this.expand(false, false);
17200             }
17201         }
17202     },
17203
17204     // private
17205     renderIndent : function(deep, refresh){
17206         if(refresh){
17207             this.ui.childIndent = null;
17208         }
17209         this.ui.renderIndent();
17210         if(deep === true && this.childrenRendered){
17211             var cs = this.childNodes;
17212             for(var i = 0, len = cs.length; i < len; i++){
17213                 cs[i].renderIndent(true, refresh);
17214             }
17215         }
17216     }
17217 });/*
17218  * Based on:
17219  * Ext JS Library 1.1.1
17220  * Copyright(c) 2006-2007, Ext JS, LLC.
17221  *
17222  * Originally Released Under LGPL - original licence link has changed is not relivant.
17223  *
17224  * Fork - LGPL
17225  * <script type="text/javascript">
17226  */
17227  
17228 /**
17229  * @class Roo.tree.AsyncTreeNode
17230  * @extends Roo.tree.TreeNode
17231  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17232  * @constructor
17233  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17234  */
17235  Roo.tree.AsyncTreeNode = function(config){
17236     this.loaded = false;
17237     this.loading = false;
17238     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17239     /**
17240     * @event beforeload
17241     * Fires before this node is loaded, return false to cancel
17242     * @param {Node} this This node
17243     */
17244     this.addEvents({'beforeload':true, 'load': true});
17245     /**
17246     * @event load
17247     * Fires when this node is loaded
17248     * @param {Node} this This node
17249     */
17250     /**
17251      * The loader used by this node (defaults to using the tree's defined loader)
17252      * @type TreeLoader
17253      * @property loader
17254      */
17255 };
17256 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17257     expand : function(deep, anim, callback){
17258         if(this.loading){ // if an async load is already running, waiting til it's done
17259             var timer;
17260             var f = function(){
17261                 if(!this.loading){ // done loading
17262                     clearInterval(timer);
17263                     this.expand(deep, anim, callback);
17264                 }
17265             }.createDelegate(this);
17266             timer = setInterval(f, 200);
17267             return;
17268         }
17269         if(!this.loaded){
17270             if(this.fireEvent("beforeload", this) === false){
17271                 return;
17272             }
17273             this.loading = true;
17274             this.ui.beforeLoad(this);
17275             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17276             if(loader){
17277                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17278                 return;
17279             }
17280         }
17281         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17282     },
17283     
17284     /**
17285      * Returns true if this node is currently loading
17286      * @return {Boolean}
17287      */
17288     isLoading : function(){
17289         return this.loading;  
17290     },
17291     
17292     loadComplete : function(deep, anim, callback){
17293         this.loading = false;
17294         this.loaded = true;
17295         this.ui.afterLoad(this);
17296         this.fireEvent("load", this);
17297         this.expand(deep, anim, callback);
17298     },
17299     
17300     /**
17301      * Returns true if this node has been loaded
17302      * @return {Boolean}
17303      */
17304     isLoaded : function(){
17305         return this.loaded;
17306     },
17307     
17308     hasChildNodes : function(){
17309         if(!this.isLeaf() && !this.loaded){
17310             return true;
17311         }else{
17312             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17313         }
17314     },
17315
17316     /**
17317      * Trigger a reload for this node
17318      * @param {Function} callback
17319      */
17320     reload : function(callback){
17321         this.collapse(false, false);
17322         while(this.firstChild){
17323             this.removeChild(this.firstChild);
17324         }
17325         this.childrenRendered = false;
17326         this.loaded = false;
17327         if(this.isHiddenRoot()){
17328             this.expanded = false;
17329         }
17330         this.expand(false, false, callback);
17331     }
17332 });/*
17333  * Based on:
17334  * Ext JS Library 1.1.1
17335  * Copyright(c) 2006-2007, Ext JS, LLC.
17336  *
17337  * Originally Released Under LGPL - original licence link has changed is not relivant.
17338  *
17339  * Fork - LGPL
17340  * <script type="text/javascript">
17341  */
17342  
17343 /**
17344  * @class Roo.tree.TreeNodeUI
17345  * @constructor
17346  * @param {Object} node The node to render
17347  * The TreeNode UI implementation is separate from the
17348  * tree implementation. Unless you are customizing the tree UI,
17349  * you should never have to use this directly.
17350  */
17351 Roo.tree.TreeNodeUI = function(node){
17352     this.node = node;
17353     this.rendered = false;
17354     this.animating = false;
17355     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17356 };
17357
17358 Roo.tree.TreeNodeUI.prototype = {
17359     removeChild : function(node){
17360         if(this.rendered){
17361             this.ctNode.removeChild(node.ui.getEl());
17362         }
17363     },
17364
17365     beforeLoad : function(){
17366          this.addClass("x-tree-node-loading");
17367     },
17368
17369     afterLoad : function(){
17370          this.removeClass("x-tree-node-loading");
17371     },
17372
17373     onTextChange : function(node, text, oldText){
17374         if(this.rendered){
17375             this.textNode.innerHTML = text;
17376         }
17377     },
17378
17379     onDisableChange : function(node, state){
17380         this.disabled = state;
17381         if(state){
17382             this.addClass("x-tree-node-disabled");
17383         }else{
17384             this.removeClass("x-tree-node-disabled");
17385         }
17386     },
17387
17388     onSelectedChange : function(state){
17389         if(state){
17390             this.focus();
17391             this.addClass("x-tree-selected");
17392         }else{
17393             //this.blur();
17394             this.removeClass("x-tree-selected");
17395         }
17396     },
17397
17398     onMove : function(tree, node, oldParent, newParent, index, refNode){
17399         this.childIndent = null;
17400         if(this.rendered){
17401             var targetNode = newParent.ui.getContainer();
17402             if(!targetNode){//target not rendered
17403                 this.holder = document.createElement("div");
17404                 this.holder.appendChild(this.wrap);
17405                 return;
17406             }
17407             var insertBefore = refNode ? refNode.ui.getEl() : null;
17408             if(insertBefore){
17409                 targetNode.insertBefore(this.wrap, insertBefore);
17410             }else{
17411                 targetNode.appendChild(this.wrap);
17412             }
17413             this.node.renderIndent(true);
17414         }
17415     },
17416
17417     addClass : function(cls){
17418         if(this.elNode){
17419             Roo.fly(this.elNode).addClass(cls);
17420         }
17421     },
17422
17423     removeClass : function(cls){
17424         if(this.elNode){
17425             Roo.fly(this.elNode).removeClass(cls);
17426         }
17427     },
17428
17429     remove : function(){
17430         if(this.rendered){
17431             this.holder = document.createElement("div");
17432             this.holder.appendChild(this.wrap);
17433         }
17434     },
17435
17436     fireEvent : function(){
17437         return this.node.fireEvent.apply(this.node, arguments);
17438     },
17439
17440     initEvents : function(){
17441         this.node.on("move", this.onMove, this);
17442         var E = Roo.EventManager;
17443         var a = this.anchor;
17444
17445         var el = Roo.fly(a, '_treeui');
17446
17447         if(Roo.isOpera){ // opera render bug ignores the CSS
17448             el.setStyle("text-decoration", "none");
17449         }
17450
17451         el.on("click", this.onClick, this);
17452         el.on("dblclick", this.onDblClick, this);
17453
17454         if(this.checkbox){
17455             Roo.EventManager.on(this.checkbox,
17456                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17457         }
17458
17459         el.on("contextmenu", this.onContextMenu, this);
17460
17461         var icon = Roo.fly(this.iconNode);
17462         icon.on("click", this.onClick, this);
17463         icon.on("dblclick", this.onDblClick, this);
17464         icon.on("contextmenu", this.onContextMenu, this);
17465         E.on(this.ecNode, "click", this.ecClick, this, true);
17466
17467         if(this.node.disabled){
17468             this.addClass("x-tree-node-disabled");
17469         }
17470         if(this.node.hidden){
17471             this.addClass("x-tree-node-disabled");
17472         }
17473         var ot = this.node.getOwnerTree();
17474         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17475         if(dd && (!this.node.isRoot || ot.rootVisible)){
17476             Roo.dd.Registry.register(this.elNode, {
17477                 node: this.node,
17478                 handles: this.getDDHandles(),
17479                 isHandle: false
17480             });
17481         }
17482     },
17483
17484     getDDHandles : function(){
17485         return [this.iconNode, this.textNode];
17486     },
17487
17488     hide : function(){
17489         if(this.rendered){
17490             this.wrap.style.display = "none";
17491         }
17492     },
17493
17494     show : function(){
17495         if(this.rendered){
17496             this.wrap.style.display = "";
17497         }
17498     },
17499
17500     onContextMenu : function(e){
17501         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17502             e.preventDefault();
17503             this.focus();
17504             this.fireEvent("contextmenu", this.node, e);
17505         }
17506     },
17507
17508     onClick : function(e){
17509         if(this.dropping){
17510             e.stopEvent();
17511             return;
17512         }
17513         if(this.fireEvent("beforeclick", this.node, e) !== false){
17514             if(!this.disabled && this.node.attributes.href){
17515                 this.fireEvent("click", this.node, e);
17516                 return;
17517             }
17518             e.preventDefault();
17519             if(this.disabled){
17520                 return;
17521             }
17522
17523             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17524                 this.node.toggle();
17525             }
17526
17527             this.fireEvent("click", this.node, e);
17528         }else{
17529             e.stopEvent();
17530         }
17531     },
17532
17533     onDblClick : function(e){
17534         e.preventDefault();
17535         if(this.disabled){
17536             return;
17537         }
17538         if(this.checkbox){
17539             this.toggleCheck();
17540         }
17541         if(!this.animating && this.node.hasChildNodes()){
17542             this.node.toggle();
17543         }
17544         this.fireEvent("dblclick", this.node, e);
17545     },
17546
17547     onCheckChange : function(){
17548         var checked = this.checkbox.checked;
17549         this.node.attributes.checked = checked;
17550         this.fireEvent('checkchange', this.node, checked);
17551     },
17552
17553     ecClick : function(e){
17554         if(!this.animating && this.node.hasChildNodes()){
17555             this.node.toggle();
17556         }
17557     },
17558
17559     startDrop : function(){
17560         this.dropping = true;
17561     },
17562
17563     // delayed drop so the click event doesn't get fired on a drop
17564     endDrop : function(){
17565        setTimeout(function(){
17566            this.dropping = false;
17567        }.createDelegate(this), 50);
17568     },
17569
17570     expand : function(){
17571         this.updateExpandIcon();
17572         this.ctNode.style.display = "";
17573     },
17574
17575     focus : function(){
17576         if(!this.node.preventHScroll){
17577             try{this.anchor.focus();
17578             }catch(e){}
17579         }else if(!Roo.isIE){
17580             try{
17581                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17582                 var l = noscroll.scrollLeft;
17583                 this.anchor.focus();
17584                 noscroll.scrollLeft = l;
17585             }catch(e){}
17586         }
17587     },
17588
17589     toggleCheck : function(value){
17590         var cb = this.checkbox;
17591         if(cb){
17592             cb.checked = (value === undefined ? !cb.checked : value);
17593         }
17594     },
17595
17596     blur : function(){
17597         try{
17598             this.anchor.blur();
17599         }catch(e){}
17600     },
17601
17602     animExpand : function(callback){
17603         var ct = Roo.get(this.ctNode);
17604         ct.stopFx();
17605         if(!this.node.hasChildNodes()){
17606             this.updateExpandIcon();
17607             this.ctNode.style.display = "";
17608             Roo.callback(callback);
17609             return;
17610         }
17611         this.animating = true;
17612         this.updateExpandIcon();
17613
17614         ct.slideIn('t', {
17615            callback : function(){
17616                this.animating = false;
17617                Roo.callback(callback);
17618             },
17619             scope: this,
17620             duration: this.node.ownerTree.duration || .25
17621         });
17622     },
17623
17624     highlight : function(){
17625         var tree = this.node.getOwnerTree();
17626         Roo.fly(this.wrap).highlight(
17627             tree.hlColor || "C3DAF9",
17628             {endColor: tree.hlBaseColor}
17629         );
17630     },
17631
17632     collapse : function(){
17633         this.updateExpandIcon();
17634         this.ctNode.style.display = "none";
17635     },
17636
17637     animCollapse : function(callback){
17638         var ct = Roo.get(this.ctNode);
17639         ct.enableDisplayMode('block');
17640         ct.stopFx();
17641
17642         this.animating = true;
17643         this.updateExpandIcon();
17644
17645         ct.slideOut('t', {
17646             callback : function(){
17647                this.animating = false;
17648                Roo.callback(callback);
17649             },
17650             scope: this,
17651             duration: this.node.ownerTree.duration || .25
17652         });
17653     },
17654
17655     getContainer : function(){
17656         return this.ctNode;
17657     },
17658
17659     getEl : function(){
17660         return this.wrap;
17661     },
17662
17663     appendDDGhost : function(ghostNode){
17664         ghostNode.appendChild(this.elNode.cloneNode(true));
17665     },
17666
17667     getDDRepairXY : function(){
17668         return Roo.lib.Dom.getXY(this.iconNode);
17669     },
17670
17671     onRender : function(){
17672         this.render();
17673     },
17674
17675     render : function(bulkRender){
17676         var n = this.node, a = n.attributes;
17677         var targetNode = n.parentNode ?
17678               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17679
17680         if(!this.rendered){
17681             this.rendered = true;
17682
17683             this.renderElements(n, a, targetNode, bulkRender);
17684
17685             if(a.qtip){
17686                if(this.textNode.setAttributeNS){
17687                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17688                    if(a.qtipTitle){
17689                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17690                    }
17691                }else{
17692                    this.textNode.setAttribute("ext:qtip", a.qtip);
17693                    if(a.qtipTitle){
17694                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17695                    }
17696                }
17697             }else if(a.qtipCfg){
17698                 a.qtipCfg.target = Roo.id(this.textNode);
17699                 Roo.QuickTips.register(a.qtipCfg);
17700             }
17701             this.initEvents();
17702             if(!this.node.expanded){
17703                 this.updateExpandIcon();
17704             }
17705         }else{
17706             if(bulkRender === true) {
17707                 targetNode.appendChild(this.wrap);
17708             }
17709         }
17710     },
17711
17712     renderElements : function(n, a, targetNode, bulkRender)
17713     {
17714         // add some indent caching, this helps performance when rendering a large tree
17715         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17716         var t = n.getOwnerTree();
17717         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17718         if (typeof(n.attributes.html) != 'undefined') {
17719             txt = n.attributes.html;
17720         }
17721         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17722         var cb = typeof a.checked == 'boolean';
17723         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17724         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17725             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17726             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17727             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17728             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17729             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17730              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17731                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17732             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17733             "</li>"];
17734
17735         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17736             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17737                                 n.nextSibling.ui.getEl(), buf.join(""));
17738         }else{
17739             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17740         }
17741
17742         this.elNode = this.wrap.childNodes[0];
17743         this.ctNode = this.wrap.childNodes[1];
17744         var cs = this.elNode.childNodes;
17745         this.indentNode = cs[0];
17746         this.ecNode = cs[1];
17747         this.iconNode = cs[2];
17748         var index = 3;
17749         if(cb){
17750             this.checkbox = cs[3];
17751             index++;
17752         }
17753         this.anchor = cs[index];
17754         this.textNode = cs[index].firstChild;
17755     },
17756
17757     getAnchor : function(){
17758         return this.anchor;
17759     },
17760
17761     getTextEl : function(){
17762         return this.textNode;
17763     },
17764
17765     getIconEl : function(){
17766         return this.iconNode;
17767     },
17768
17769     isChecked : function(){
17770         return this.checkbox ? this.checkbox.checked : false;
17771     },
17772
17773     updateExpandIcon : function(){
17774         if(this.rendered){
17775             var n = this.node, c1, c2;
17776             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17777             var hasChild = n.hasChildNodes();
17778             if(hasChild){
17779                 if(n.expanded){
17780                     cls += "-minus";
17781                     c1 = "x-tree-node-collapsed";
17782                     c2 = "x-tree-node-expanded";
17783                 }else{
17784                     cls += "-plus";
17785                     c1 = "x-tree-node-expanded";
17786                     c2 = "x-tree-node-collapsed";
17787                 }
17788                 if(this.wasLeaf){
17789                     this.removeClass("x-tree-node-leaf");
17790                     this.wasLeaf = false;
17791                 }
17792                 if(this.c1 != c1 || this.c2 != c2){
17793                     Roo.fly(this.elNode).replaceClass(c1, c2);
17794                     this.c1 = c1; this.c2 = c2;
17795                 }
17796             }else{
17797                 // this changes non-leafs into leafs if they have no children.
17798                 // it's not very rational behaviour..
17799                 
17800                 if(!this.wasLeaf && this.node.leaf){
17801                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17802                     delete this.c1;
17803                     delete this.c2;
17804                     this.wasLeaf = true;
17805                 }
17806             }
17807             var ecc = "x-tree-ec-icon "+cls;
17808             if(this.ecc != ecc){
17809                 this.ecNode.className = ecc;
17810                 this.ecc = ecc;
17811             }
17812         }
17813     },
17814
17815     getChildIndent : function(){
17816         if(!this.childIndent){
17817             var buf = [];
17818             var p = this.node;
17819             while(p){
17820                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17821                     if(!p.isLast()) {
17822                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17823                     } else {
17824                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17825                     }
17826                 }
17827                 p = p.parentNode;
17828             }
17829             this.childIndent = buf.join("");
17830         }
17831         return this.childIndent;
17832     },
17833
17834     renderIndent : function(){
17835         if(this.rendered){
17836             var indent = "";
17837             var p = this.node.parentNode;
17838             if(p){
17839                 indent = p.ui.getChildIndent();
17840             }
17841             if(this.indentMarkup != indent){ // don't rerender if not required
17842                 this.indentNode.innerHTML = indent;
17843                 this.indentMarkup = indent;
17844             }
17845             this.updateExpandIcon();
17846         }
17847     }
17848 };
17849
17850 Roo.tree.RootTreeNodeUI = function(){
17851     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17852 };
17853 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17854     render : function(){
17855         if(!this.rendered){
17856             var targetNode = this.node.ownerTree.innerCt.dom;
17857             this.node.expanded = true;
17858             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17859             this.wrap = this.ctNode = targetNode.firstChild;
17860         }
17861     },
17862     collapse : function(){
17863     },
17864     expand : function(){
17865     }
17866 });/*
17867  * Based on:
17868  * Ext JS Library 1.1.1
17869  * Copyright(c) 2006-2007, Ext JS, LLC.
17870  *
17871  * Originally Released Under LGPL - original licence link has changed is not relivant.
17872  *
17873  * Fork - LGPL
17874  * <script type="text/javascript">
17875  */
17876 /**
17877  * @class Roo.tree.TreeLoader
17878  * @extends Roo.util.Observable
17879  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17880  * nodes from a specified URL. The response must be a javascript Array definition
17881  * who's elements are node definition objects. eg:
17882  * <pre><code>
17883 {  success : true,
17884    data :      [
17885    
17886     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17887     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17888     ]
17889 }
17890
17891
17892 </code></pre>
17893  * <br><br>
17894  * The old style respose with just an array is still supported, but not recommended.
17895  * <br><br>
17896  *
17897  * A server request is sent, and child nodes are loaded only when a node is expanded.
17898  * The loading node's id is passed to the server under the parameter name "node" to
17899  * enable the server to produce the correct child nodes.
17900  * <br><br>
17901  * To pass extra parameters, an event handler may be attached to the "beforeload"
17902  * event, and the parameters specified in the TreeLoader's baseParams property:
17903  * <pre><code>
17904     myTreeLoader.on("beforeload", function(treeLoader, node) {
17905         this.baseParams.category = node.attributes.category;
17906     }, this);
17907 </code></pre><
17908  * This would pass an HTTP parameter called "category" to the server containing
17909  * the value of the Node's "category" attribute.
17910  * @constructor
17911  * Creates a new Treeloader.
17912  * @param {Object} config A config object containing config properties.
17913  */
17914 Roo.tree.TreeLoader = function(config){
17915     this.baseParams = {};
17916     this.requestMethod = "POST";
17917     Roo.apply(this, config);
17918
17919     this.addEvents({
17920     
17921         /**
17922          * @event beforeload
17923          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17924          * @param {Object} This TreeLoader object.
17925          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17926          * @param {Object} callback The callback function specified in the {@link #load} call.
17927          */
17928         beforeload : true,
17929         /**
17930          * @event load
17931          * Fires when the node has been successfuly loaded.
17932          * @param {Object} This TreeLoader object.
17933          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17934          * @param {Object} response The response object containing the data from the server.
17935          */
17936         load : true,
17937         /**
17938          * @event loadexception
17939          * Fires if the network request failed.
17940          * @param {Object} This TreeLoader object.
17941          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17942          * @param {Object} response The response object containing the data from the server.
17943          */
17944         loadexception : true,
17945         /**
17946          * @event create
17947          * Fires before a node is created, enabling you to return custom Node types 
17948          * @param {Object} This TreeLoader object.
17949          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17950          */
17951         create : true
17952     });
17953
17954     Roo.tree.TreeLoader.superclass.constructor.call(this);
17955 };
17956
17957 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17958     /**
17959     * @cfg {String} dataUrl The URL from which to request a Json string which
17960     * specifies an array of node definition object representing the child nodes
17961     * to be loaded.
17962     */
17963     /**
17964     * @cfg {String} requestMethod either GET or POST
17965     * defaults to POST (due to BC)
17966     * to be loaded.
17967     */
17968     /**
17969     * @cfg {Object} baseParams (optional) An object containing properties which
17970     * specify HTTP parameters to be passed to each request for child nodes.
17971     */
17972     /**
17973     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17974     * created by this loader. If the attributes sent by the server have an attribute in this object,
17975     * they take priority.
17976     */
17977     /**
17978     * @cfg {Object} uiProviders (optional) An object containing properties which
17979     * 
17980     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17981     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17982     * <i>uiProvider</i> attribute of a returned child node is a string rather
17983     * than a reference to a TreeNodeUI implementation, this that string value
17984     * is used as a property name in the uiProviders object. You can define the provider named
17985     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
17986     */
17987     uiProviders : {},
17988
17989     /**
17990     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
17991     * child nodes before loading.
17992     */
17993     clearOnLoad : true,
17994
17995     /**
17996     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
17997     * property on loading, rather than expecting an array. (eg. more compatible to a standard
17998     * Grid query { data : [ .....] }
17999     */
18000     
18001     root : false,
18002      /**
18003     * @cfg {String} queryParam (optional) 
18004     * Name of the query as it will be passed on the querystring (defaults to 'node')
18005     * eg. the request will be ?node=[id]
18006     */
18007     
18008     
18009     queryParam: false,
18010     
18011     /**
18012      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18013      * This is called automatically when a node is expanded, but may be used to reload
18014      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18015      * @param {Roo.tree.TreeNode} node
18016      * @param {Function} callback
18017      */
18018     load : function(node, callback){
18019         if(this.clearOnLoad){
18020             while(node.firstChild){
18021                 node.removeChild(node.firstChild);
18022             }
18023         }
18024         if(node.attributes.children){ // preloaded json children
18025             var cs = node.attributes.children;
18026             for(var i = 0, len = cs.length; i < len; i++){
18027                 node.appendChild(this.createNode(cs[i]));
18028             }
18029             if(typeof callback == "function"){
18030                 callback();
18031             }
18032         }else if(this.dataUrl){
18033             this.requestData(node, callback);
18034         }
18035     },
18036
18037     getParams: function(node){
18038         var buf = [], bp = this.baseParams;
18039         for(var key in bp){
18040             if(typeof bp[key] != "function"){
18041                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18042             }
18043         }
18044         var n = this.queryParam === false ? 'node' : this.queryParam;
18045         buf.push(n + "=", encodeURIComponent(node.id));
18046         return buf.join("");
18047     },
18048
18049     requestData : function(node, callback){
18050         if(this.fireEvent("beforeload", this, node, callback) !== false){
18051             this.transId = Roo.Ajax.request({
18052                 method:this.requestMethod,
18053                 url: this.dataUrl||this.url,
18054                 success: this.handleResponse,
18055                 failure: this.handleFailure,
18056                 scope: this,
18057                 argument: {callback: callback, node: node},
18058                 params: this.getParams(node)
18059             });
18060         }else{
18061             // if the load is cancelled, make sure we notify
18062             // the node that we are done
18063             if(typeof callback == "function"){
18064                 callback();
18065             }
18066         }
18067     },
18068
18069     isLoading : function(){
18070         return this.transId ? true : false;
18071     },
18072
18073     abort : function(){
18074         if(this.isLoading()){
18075             Roo.Ajax.abort(this.transId);
18076         }
18077     },
18078
18079     // private
18080     createNode : function(attr)
18081     {
18082         // apply baseAttrs, nice idea Corey!
18083         if(this.baseAttrs){
18084             Roo.applyIf(attr, this.baseAttrs);
18085         }
18086         if(this.applyLoader !== false){
18087             attr.loader = this;
18088         }
18089         // uiProvider = depreciated..
18090         
18091         if(typeof(attr.uiProvider) == 'string'){
18092            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18093                 /**  eval:var:attr */ eval(attr.uiProvider);
18094         }
18095         if(typeof(this.uiProviders['default']) != 'undefined') {
18096             attr.uiProvider = this.uiProviders['default'];
18097         }
18098         
18099         this.fireEvent('create', this, attr);
18100         
18101         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18102         return(attr.leaf ?
18103                         new Roo.tree.TreeNode(attr) :
18104                         new Roo.tree.AsyncTreeNode(attr));
18105     },
18106
18107     processResponse : function(response, node, callback)
18108     {
18109         var json = response.responseText;
18110         try {
18111             
18112             var o = Roo.decode(json);
18113             
18114             if (this.root === false && typeof(o.success) != undefined) {
18115                 this.root = 'data'; // the default behaviour for list like data..
18116                 }
18117                 
18118             if (this.root !== false &&  !o.success) {
18119                 // it's a failure condition.
18120                 var a = response.argument;
18121                 this.fireEvent("loadexception", this, a.node, response);
18122                 Roo.log("Load failed - should have a handler really");
18123                 return;
18124             }
18125             
18126             
18127             
18128             if (this.root !== false) {
18129                  o = o[this.root];
18130             }
18131             
18132             for(var i = 0, len = o.length; i < len; i++){
18133                 var n = this.createNode(o[i]);
18134                 if(n){
18135                     node.appendChild(n);
18136                 }
18137             }
18138             if(typeof callback == "function"){
18139                 callback(this, node);
18140             }
18141         }catch(e){
18142             this.handleFailure(response);
18143         }
18144     },
18145
18146     handleResponse : function(response){
18147         this.transId = false;
18148         var a = response.argument;
18149         this.processResponse(response, a.node, a.callback);
18150         this.fireEvent("load", this, a.node, response);
18151     },
18152
18153     handleFailure : function(response)
18154     {
18155         // should handle failure better..
18156         this.transId = false;
18157         var a = response.argument;
18158         this.fireEvent("loadexception", this, a.node, response);
18159         if(typeof a.callback == "function"){
18160             a.callback(this, a.node);
18161         }
18162     }
18163 });/*
18164  * Based on:
18165  * Ext JS Library 1.1.1
18166  * Copyright(c) 2006-2007, Ext JS, LLC.
18167  *
18168  * Originally Released Under LGPL - original licence link has changed is not relivant.
18169  *
18170  * Fork - LGPL
18171  * <script type="text/javascript">
18172  */
18173
18174 /**
18175 * @class Roo.tree.TreeFilter
18176 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18177 * @param {TreePanel} tree
18178 * @param {Object} config (optional)
18179  */
18180 Roo.tree.TreeFilter = function(tree, config){
18181     this.tree = tree;
18182     this.filtered = {};
18183     Roo.apply(this, config);
18184 };
18185
18186 Roo.tree.TreeFilter.prototype = {
18187     clearBlank:false,
18188     reverse:false,
18189     autoClear:false,
18190     remove:false,
18191
18192      /**
18193      * Filter the data by a specific attribute.
18194      * @param {String/RegExp} value Either string that the attribute value
18195      * should start with or a RegExp to test against the attribute
18196      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18197      * @param {TreeNode} startNode (optional) The node to start the filter at.
18198      */
18199     filter : function(value, attr, startNode){
18200         attr = attr || "text";
18201         var f;
18202         if(typeof value == "string"){
18203             var vlen = value.length;
18204             // auto clear empty filter
18205             if(vlen == 0 && this.clearBlank){
18206                 this.clear();
18207                 return;
18208             }
18209             value = value.toLowerCase();
18210             f = function(n){
18211                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18212             };
18213         }else if(value.exec){ // regex?
18214             f = function(n){
18215                 return value.test(n.attributes[attr]);
18216             };
18217         }else{
18218             throw 'Illegal filter type, must be string or regex';
18219         }
18220         this.filterBy(f, null, startNode);
18221         },
18222
18223     /**
18224      * Filter by a function. The passed function will be called with each
18225      * node in the tree (or from the startNode). If the function returns true, the node is kept
18226      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18227      * @param {Function} fn The filter function
18228      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18229      */
18230     filterBy : function(fn, scope, startNode){
18231         startNode = startNode || this.tree.root;
18232         if(this.autoClear){
18233             this.clear();
18234         }
18235         var af = this.filtered, rv = this.reverse;
18236         var f = function(n){
18237             if(n == startNode){
18238                 return true;
18239             }
18240             if(af[n.id]){
18241                 return false;
18242             }
18243             var m = fn.call(scope || n, n);
18244             if(!m || rv){
18245                 af[n.id] = n;
18246                 n.ui.hide();
18247                 return false;
18248             }
18249             return true;
18250         };
18251         startNode.cascade(f);
18252         if(this.remove){
18253            for(var id in af){
18254                if(typeof id != "function"){
18255                    var n = af[id];
18256                    if(n && n.parentNode){
18257                        n.parentNode.removeChild(n);
18258                    }
18259                }
18260            }
18261         }
18262     },
18263
18264     /**
18265      * Clears the current filter. Note: with the "remove" option
18266      * set a filter cannot be cleared.
18267      */
18268     clear : function(){
18269         var t = this.tree;
18270         var af = this.filtered;
18271         for(var id in af){
18272             if(typeof id != "function"){
18273                 var n = af[id];
18274                 if(n){
18275                     n.ui.show();
18276                 }
18277             }
18278         }
18279         this.filtered = {};
18280     }
18281 };
18282 /*
18283  * Based on:
18284  * Ext JS Library 1.1.1
18285  * Copyright(c) 2006-2007, Ext JS, LLC.
18286  *
18287  * Originally Released Under LGPL - original licence link has changed is not relivant.
18288  *
18289  * Fork - LGPL
18290  * <script type="text/javascript">
18291  */
18292  
18293
18294 /**
18295  * @class Roo.tree.TreeSorter
18296  * Provides sorting of nodes in a TreePanel
18297  * 
18298  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18299  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18300  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18301  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18302  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18303  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18304  * @constructor
18305  * @param {TreePanel} tree
18306  * @param {Object} config
18307  */
18308 Roo.tree.TreeSorter = function(tree, config){
18309     Roo.apply(this, config);
18310     tree.on("beforechildrenrendered", this.doSort, this);
18311     tree.on("append", this.updateSort, this);
18312     tree.on("insert", this.updateSort, this);
18313     
18314     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18315     var p = this.property || "text";
18316     var sortType = this.sortType;
18317     var fs = this.folderSort;
18318     var cs = this.caseSensitive === true;
18319     var leafAttr = this.leafAttr || 'leaf';
18320
18321     this.sortFn = function(n1, n2){
18322         if(fs){
18323             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18324                 return 1;
18325             }
18326             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18327                 return -1;
18328             }
18329         }
18330         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18331         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18332         if(v1 < v2){
18333                         return dsc ? +1 : -1;
18334                 }else if(v1 > v2){
18335                         return dsc ? -1 : +1;
18336         }else{
18337                 return 0;
18338         }
18339     };
18340 };
18341
18342 Roo.tree.TreeSorter.prototype = {
18343     doSort : function(node){
18344         node.sort(this.sortFn);
18345     },
18346     
18347     compareNodes : function(n1, n2){
18348         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18349     },
18350     
18351     updateSort : function(tree, node){
18352         if(node.childrenRendered){
18353             this.doSort.defer(1, this, [node]);
18354         }
18355     }
18356 };/*
18357  * Based on:
18358  * Ext JS Library 1.1.1
18359  * Copyright(c) 2006-2007, Ext JS, LLC.
18360  *
18361  * Originally Released Under LGPL - original licence link has changed is not relivant.
18362  *
18363  * Fork - LGPL
18364  * <script type="text/javascript">
18365  */
18366
18367 if(Roo.dd.DropZone){
18368     
18369 Roo.tree.TreeDropZone = function(tree, config){
18370     this.allowParentInsert = false;
18371     this.allowContainerDrop = false;
18372     this.appendOnly = false;
18373     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18374     this.tree = tree;
18375     this.lastInsertClass = "x-tree-no-status";
18376     this.dragOverData = {};
18377 };
18378
18379 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18380     ddGroup : "TreeDD",
18381     scroll:  true,
18382     
18383     expandDelay : 1000,
18384     
18385     expandNode : function(node){
18386         if(node.hasChildNodes() && !node.isExpanded()){
18387             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18388         }
18389     },
18390     
18391     queueExpand : function(node){
18392         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18393     },
18394     
18395     cancelExpand : function(){
18396         if(this.expandProcId){
18397             clearTimeout(this.expandProcId);
18398             this.expandProcId = false;
18399         }
18400     },
18401     
18402     isValidDropPoint : function(n, pt, dd, e, data){
18403         if(!n || !data){ return false; }
18404         var targetNode = n.node;
18405         var dropNode = data.node;
18406         // default drop rules
18407         if(!(targetNode && targetNode.isTarget && pt)){
18408             return false;
18409         }
18410         if(pt == "append" && targetNode.allowChildren === false){
18411             return false;
18412         }
18413         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18414             return false;
18415         }
18416         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18417             return false;
18418         }
18419         // reuse the object
18420         var overEvent = this.dragOverData;
18421         overEvent.tree = this.tree;
18422         overEvent.target = targetNode;
18423         overEvent.data = data;
18424         overEvent.point = pt;
18425         overEvent.source = dd;
18426         overEvent.rawEvent = e;
18427         overEvent.dropNode = dropNode;
18428         overEvent.cancel = false;  
18429         var result = this.tree.fireEvent("nodedragover", overEvent);
18430         return overEvent.cancel === false && result !== false;
18431     },
18432     
18433     getDropPoint : function(e, n, dd)
18434     {
18435         var tn = n.node;
18436         if(tn.isRoot){
18437             return tn.allowChildren !== false ? "append" : false; // always append for root
18438         }
18439         var dragEl = n.ddel;
18440         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18441         var y = Roo.lib.Event.getPageY(e);
18442         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18443         
18444         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18445         var noAppend = tn.allowChildren === false;
18446         if(this.appendOnly || tn.parentNode.allowChildren === false){
18447             return noAppend ? false : "append";
18448         }
18449         var noBelow = false;
18450         if(!this.allowParentInsert){
18451             noBelow = tn.hasChildNodes() && tn.isExpanded();
18452         }
18453         var q = (b - t) / (noAppend ? 2 : 3);
18454         if(y >= t && y < (t + q)){
18455             return "above";
18456         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18457             return "below";
18458         }else{
18459             return "append";
18460         }
18461     },
18462     
18463     onNodeEnter : function(n, dd, e, data)
18464     {
18465         this.cancelExpand();
18466     },
18467     
18468     onNodeOver : function(n, dd, e, data)
18469     {
18470        
18471         var pt = this.getDropPoint(e, n, dd);
18472         var node = n.node;
18473         
18474         // auto node expand check
18475         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18476             this.queueExpand(node);
18477         }else if(pt != "append"){
18478             this.cancelExpand();
18479         }
18480         
18481         // set the insert point style on the target node
18482         var returnCls = this.dropNotAllowed;
18483         if(this.isValidDropPoint(n, pt, dd, e, data)){
18484            if(pt){
18485                var el = n.ddel;
18486                var cls;
18487                if(pt == "above"){
18488                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18489                    cls = "x-tree-drag-insert-above";
18490                }else if(pt == "below"){
18491                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18492                    cls = "x-tree-drag-insert-below";
18493                }else{
18494                    returnCls = "x-tree-drop-ok-append";
18495                    cls = "x-tree-drag-append";
18496                }
18497                if(this.lastInsertClass != cls){
18498                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18499                    this.lastInsertClass = cls;
18500                }
18501            }
18502        }
18503        return returnCls;
18504     },
18505     
18506     onNodeOut : function(n, dd, e, data){
18507         
18508         this.cancelExpand();
18509         this.removeDropIndicators(n);
18510     },
18511     
18512     onNodeDrop : function(n, dd, e, data){
18513         var point = this.getDropPoint(e, n, dd);
18514         var targetNode = n.node;
18515         targetNode.ui.startDrop();
18516         if(!this.isValidDropPoint(n, point, dd, e, data)){
18517             targetNode.ui.endDrop();
18518             return false;
18519         }
18520         // first try to find the drop node
18521         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18522         var dropEvent = {
18523             tree : this.tree,
18524             target: targetNode,
18525             data: data,
18526             point: point,
18527             source: dd,
18528             rawEvent: e,
18529             dropNode: dropNode,
18530             cancel: !dropNode   
18531         };
18532         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18533         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18534             targetNode.ui.endDrop();
18535             return false;
18536         }
18537         // allow target changing
18538         targetNode = dropEvent.target;
18539         if(point == "append" && !targetNode.isExpanded()){
18540             targetNode.expand(false, null, function(){
18541                 this.completeDrop(dropEvent);
18542             }.createDelegate(this));
18543         }else{
18544             this.completeDrop(dropEvent);
18545         }
18546         return true;
18547     },
18548     
18549     completeDrop : function(de){
18550         var ns = de.dropNode, p = de.point, t = de.target;
18551         if(!(ns instanceof Array)){
18552             ns = [ns];
18553         }
18554         var n;
18555         for(var i = 0, len = ns.length; i < len; i++){
18556             n = ns[i];
18557             if(p == "above"){
18558                 t.parentNode.insertBefore(n, t);
18559             }else if(p == "below"){
18560                 t.parentNode.insertBefore(n, t.nextSibling);
18561             }else{
18562                 t.appendChild(n);
18563             }
18564         }
18565         n.ui.focus();
18566         if(this.tree.hlDrop){
18567             n.ui.highlight();
18568         }
18569         t.ui.endDrop();
18570         this.tree.fireEvent("nodedrop", de);
18571     },
18572     
18573     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18574         if(this.tree.hlDrop){
18575             dropNode.ui.focus();
18576             dropNode.ui.highlight();
18577         }
18578         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18579     },
18580     
18581     getTree : function(){
18582         return this.tree;
18583     },
18584     
18585     removeDropIndicators : function(n){
18586         if(n && n.ddel){
18587             var el = n.ddel;
18588             Roo.fly(el).removeClass([
18589                     "x-tree-drag-insert-above",
18590                     "x-tree-drag-insert-below",
18591                     "x-tree-drag-append"]);
18592             this.lastInsertClass = "_noclass";
18593         }
18594     },
18595     
18596     beforeDragDrop : function(target, e, id){
18597         this.cancelExpand();
18598         return true;
18599     },
18600     
18601     afterRepair : function(data){
18602         if(data && Roo.enableFx){
18603             data.node.ui.highlight();
18604         }
18605         this.hideProxy();
18606     } 
18607     
18608 });
18609
18610 }
18611 /*
18612  * Based on:
18613  * Ext JS Library 1.1.1
18614  * Copyright(c) 2006-2007, Ext JS, LLC.
18615  *
18616  * Originally Released Under LGPL - original licence link has changed is not relivant.
18617  *
18618  * Fork - LGPL
18619  * <script type="text/javascript">
18620  */
18621  
18622
18623 if(Roo.dd.DragZone){
18624 Roo.tree.TreeDragZone = function(tree, config){
18625     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18626     this.tree = tree;
18627 };
18628
18629 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18630     ddGroup : "TreeDD",
18631    
18632     onBeforeDrag : function(data, e){
18633         var n = data.node;
18634         return n && n.draggable && !n.disabled;
18635     },
18636      
18637     
18638     onInitDrag : function(e){
18639         var data = this.dragData;
18640         this.tree.getSelectionModel().select(data.node);
18641         this.proxy.update("");
18642         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18643         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18644     },
18645     
18646     getRepairXY : function(e, data){
18647         return data.node.ui.getDDRepairXY();
18648     },
18649     
18650     onEndDrag : function(data, e){
18651         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18652         
18653         
18654     },
18655     
18656     onValidDrop : function(dd, e, id){
18657         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18658         this.hideProxy();
18659     },
18660     
18661     beforeInvalidDrop : function(e, id){
18662         // this scrolls the original position back into view
18663         var sm = this.tree.getSelectionModel();
18664         sm.clearSelections();
18665         sm.select(this.dragData.node);
18666     }
18667 });
18668 }/*
18669  * Based on:
18670  * Ext JS Library 1.1.1
18671  * Copyright(c) 2006-2007, Ext JS, LLC.
18672  *
18673  * Originally Released Under LGPL - original licence link has changed is not relivant.
18674  *
18675  * Fork - LGPL
18676  * <script type="text/javascript">
18677  */
18678 /**
18679  * @class Roo.tree.TreeEditor
18680  * @extends Roo.Editor
18681  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18682  * as the editor field.
18683  * @constructor
18684  * @param {Object} config (used to be the tree panel.)
18685  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18686  * 
18687  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18688  * @cfg {Roo.form.TextField|Object} field The field configuration
18689  *
18690  * 
18691  */
18692 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18693     var tree = config;
18694     var field;
18695     if (oldconfig) { // old style..
18696         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18697     } else {
18698         // new style..
18699         tree = config.tree;
18700         config.field = config.field  || {};
18701         config.field.xtype = 'TextField';
18702         field = Roo.factory(config.field, Roo.form);
18703     }
18704     config = config || {};
18705     
18706     
18707     this.addEvents({
18708         /**
18709          * @event beforenodeedit
18710          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18711          * false from the handler of this event.
18712          * @param {Editor} this
18713          * @param {Roo.tree.Node} node 
18714          */
18715         "beforenodeedit" : true
18716     });
18717     
18718     //Roo.log(config);
18719     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18720
18721     this.tree = tree;
18722
18723     tree.on('beforeclick', this.beforeNodeClick, this);
18724     tree.getTreeEl().on('mousedown', this.hide, this);
18725     this.on('complete', this.updateNode, this);
18726     this.on('beforestartedit', this.fitToTree, this);
18727     this.on('startedit', this.bindScroll, this, {delay:10});
18728     this.on('specialkey', this.onSpecialKey, this);
18729 };
18730
18731 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18732     /**
18733      * @cfg {String} alignment
18734      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18735      */
18736     alignment: "l-l",
18737     // inherit
18738     autoSize: false,
18739     /**
18740      * @cfg {Boolean} hideEl
18741      * True to hide the bound element while the editor is displayed (defaults to false)
18742      */
18743     hideEl : false,
18744     /**
18745      * @cfg {String} cls
18746      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18747      */
18748     cls: "x-small-editor x-tree-editor",
18749     /**
18750      * @cfg {Boolean} shim
18751      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18752      */
18753     shim:false,
18754     // inherit
18755     shadow:"frame",
18756     /**
18757      * @cfg {Number} maxWidth
18758      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18759      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18760      * scroll and client offsets into account prior to each edit.
18761      */
18762     maxWidth: 250,
18763
18764     editDelay : 350,
18765
18766     // private
18767     fitToTree : function(ed, el){
18768         var td = this.tree.getTreeEl().dom, nd = el.dom;
18769         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18770             td.scrollLeft = nd.offsetLeft;
18771         }
18772         var w = Math.min(
18773                 this.maxWidth,
18774                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18775         this.setSize(w, '');
18776         
18777         return this.fireEvent('beforenodeedit', this, this.editNode);
18778         
18779     },
18780
18781     // private
18782     triggerEdit : function(node){
18783         this.completeEdit();
18784         this.editNode = node;
18785         this.startEdit(node.ui.textNode, node.text);
18786     },
18787
18788     // private
18789     bindScroll : function(){
18790         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18791     },
18792
18793     // private
18794     beforeNodeClick : function(node, e){
18795         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18796         this.lastClick = new Date();
18797         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18798             e.stopEvent();
18799             this.triggerEdit(node);
18800             return false;
18801         }
18802         return true;
18803     },
18804
18805     // private
18806     updateNode : function(ed, value){
18807         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18808         this.editNode.setText(value);
18809     },
18810
18811     // private
18812     onHide : function(){
18813         Roo.tree.TreeEditor.superclass.onHide.call(this);
18814         if(this.editNode){
18815             this.editNode.ui.focus();
18816         }
18817     },
18818
18819     // private
18820     onSpecialKey : function(field, e){
18821         var k = e.getKey();
18822         if(k == e.ESC){
18823             e.stopEvent();
18824             this.cancelEdit();
18825         }else if(k == e.ENTER && !e.hasModifier()){
18826             e.stopEvent();
18827             this.completeEdit();
18828         }
18829     }
18830 });//<Script type="text/javascript">
18831 /*
18832  * Based on:
18833  * Ext JS Library 1.1.1
18834  * Copyright(c) 2006-2007, Ext JS, LLC.
18835  *
18836  * Originally Released Under LGPL - original licence link has changed is not relivant.
18837  *
18838  * Fork - LGPL
18839  * <script type="text/javascript">
18840  */
18841  
18842 /**
18843  * Not documented??? - probably should be...
18844  */
18845
18846 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18847     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18848     
18849     renderElements : function(n, a, targetNode, bulkRender){
18850         //consel.log("renderElements?");
18851         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18852
18853         var t = n.getOwnerTree();
18854         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18855         
18856         var cols = t.columns;
18857         var bw = t.borderWidth;
18858         var c = cols[0];
18859         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18860          var cb = typeof a.checked == "boolean";
18861         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18862         var colcls = 'x-t-' + tid + '-c0';
18863         var buf = [
18864             '<li class="x-tree-node">',
18865             
18866                 
18867                 '<div class="x-tree-node-el ', a.cls,'">',
18868                     // extran...
18869                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18870                 
18871                 
18872                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18873                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18874                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18875                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18876                            (a.iconCls ? ' '+a.iconCls : ''),
18877                            '" unselectable="on" />',
18878                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18879                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18880                              
18881                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18882                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18883                             '<span unselectable="on" qtip="' + tx + '">',
18884                              tx,
18885                              '</span></a>' ,
18886                     '</div>',
18887                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18888                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18889                  ];
18890         for(var i = 1, len = cols.length; i < len; i++){
18891             c = cols[i];
18892             colcls = 'x-t-' + tid + '-c' +i;
18893             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18894             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18895                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18896                       "</div>");
18897          }
18898          
18899          buf.push(
18900             '</a>',
18901             '<div class="x-clear"></div></div>',
18902             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18903             "</li>");
18904         
18905         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18906             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18907                                 n.nextSibling.ui.getEl(), buf.join(""));
18908         }else{
18909             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18910         }
18911         var el = this.wrap.firstChild;
18912         this.elRow = el;
18913         this.elNode = el.firstChild;
18914         this.ranchor = el.childNodes[1];
18915         this.ctNode = this.wrap.childNodes[1];
18916         var cs = el.firstChild.childNodes;
18917         this.indentNode = cs[0];
18918         this.ecNode = cs[1];
18919         this.iconNode = cs[2];
18920         var index = 3;
18921         if(cb){
18922             this.checkbox = cs[3];
18923             index++;
18924         }
18925         this.anchor = cs[index];
18926         
18927         this.textNode = cs[index].firstChild;
18928         
18929         //el.on("click", this.onClick, this);
18930         //el.on("dblclick", this.onDblClick, this);
18931         
18932         
18933        // console.log(this);
18934     },
18935     initEvents : function(){
18936         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18937         
18938             
18939         var a = this.ranchor;
18940
18941         var el = Roo.get(a);
18942
18943         if(Roo.isOpera){ // opera render bug ignores the CSS
18944             el.setStyle("text-decoration", "none");
18945         }
18946
18947         el.on("click", this.onClick, this);
18948         el.on("dblclick", this.onDblClick, this);
18949         el.on("contextmenu", this.onContextMenu, this);
18950         
18951     },
18952     
18953     /*onSelectedChange : function(state){
18954         if(state){
18955             this.focus();
18956             this.addClass("x-tree-selected");
18957         }else{
18958             //this.blur();
18959             this.removeClass("x-tree-selected");
18960         }
18961     },*/
18962     addClass : function(cls){
18963         if(this.elRow){
18964             Roo.fly(this.elRow).addClass(cls);
18965         }
18966         
18967     },
18968     
18969     
18970     removeClass : function(cls){
18971         if(this.elRow){
18972             Roo.fly(this.elRow).removeClass(cls);
18973         }
18974     }
18975
18976     
18977     
18978 });//<Script type="text/javascript">
18979
18980 /*
18981  * Based on:
18982  * Ext JS Library 1.1.1
18983  * Copyright(c) 2006-2007, Ext JS, LLC.
18984  *
18985  * Originally Released Under LGPL - original licence link has changed is not relivant.
18986  *
18987  * Fork - LGPL
18988  * <script type="text/javascript">
18989  */
18990  
18991
18992 /**
18993  * @class Roo.tree.ColumnTree
18994  * @extends Roo.data.TreePanel
18995  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
18996  * @cfg {int} borderWidth  compined right/left border allowance
18997  * @constructor
18998  * @param {String/HTMLElement/Element} el The container element
18999  * @param {Object} config
19000  */
19001 Roo.tree.ColumnTree =  function(el, config)
19002 {
19003    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19004    this.addEvents({
19005         /**
19006         * @event resize
19007         * Fire this event on a container when it resizes
19008         * @param {int} w Width
19009         * @param {int} h Height
19010         */
19011        "resize" : true
19012     });
19013     this.on('resize', this.onResize, this);
19014 };
19015
19016 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19017     //lines:false,
19018     
19019     
19020     borderWidth: Roo.isBorderBox ? 0 : 2, 
19021     headEls : false,
19022     
19023     render : function(){
19024         // add the header.....
19025        
19026         Roo.tree.ColumnTree.superclass.render.apply(this);
19027         
19028         this.el.addClass('x-column-tree');
19029         
19030         this.headers = this.el.createChild(
19031             {cls:'x-tree-headers'},this.innerCt.dom);
19032    
19033         var cols = this.columns, c;
19034         var totalWidth = 0;
19035         this.headEls = [];
19036         var  len = cols.length;
19037         for(var i = 0; i < len; i++){
19038              c = cols[i];
19039              totalWidth += c.width;
19040             this.headEls.push(this.headers.createChild({
19041                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19042                  cn: {
19043                      cls:'x-tree-hd-text',
19044                      html: c.header
19045                  },
19046                  style:'width:'+(c.width-this.borderWidth)+'px;'
19047              }));
19048         }
19049         this.headers.createChild({cls:'x-clear'});
19050         // prevent floats from wrapping when clipped
19051         this.headers.setWidth(totalWidth);
19052         //this.innerCt.setWidth(totalWidth);
19053         this.innerCt.setStyle({ overflow: 'auto' });
19054         this.onResize(this.width, this.height);
19055              
19056         
19057     },
19058     onResize : function(w,h)
19059     {
19060         this.height = h;
19061         this.width = w;
19062         // resize cols..
19063         this.innerCt.setWidth(this.width);
19064         this.innerCt.setHeight(this.height-20);
19065         
19066         // headers...
19067         var cols = this.columns, c;
19068         var totalWidth = 0;
19069         var expEl = false;
19070         var len = cols.length;
19071         for(var i = 0; i < len; i++){
19072             c = cols[i];
19073             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19074                 // it's the expander..
19075                 expEl  = this.headEls[i];
19076                 continue;
19077             }
19078             totalWidth += c.width;
19079             
19080         }
19081         if (expEl) {
19082             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19083         }
19084         this.headers.setWidth(w-20);
19085
19086         
19087         
19088         
19089     }
19090 });
19091 /*
19092  * Based on:
19093  * Ext JS Library 1.1.1
19094  * Copyright(c) 2006-2007, Ext JS, LLC.
19095  *
19096  * Originally Released Under LGPL - original licence link has changed is not relivant.
19097  *
19098  * Fork - LGPL
19099  * <script type="text/javascript">
19100  */
19101  
19102 /**
19103  * @class Roo.menu.Menu
19104  * @extends Roo.util.Observable
19105  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19106  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19107  * @constructor
19108  * Creates a new Menu
19109  * @param {Object} config Configuration options
19110  */
19111 Roo.menu.Menu = function(config){
19112     Roo.apply(this, config);
19113     this.id = this.id || Roo.id();
19114     this.addEvents({
19115         /**
19116          * @event beforeshow
19117          * Fires before this menu is displayed
19118          * @param {Roo.menu.Menu} this
19119          */
19120         beforeshow : true,
19121         /**
19122          * @event beforehide
19123          * Fires before this menu is hidden
19124          * @param {Roo.menu.Menu} this
19125          */
19126         beforehide : true,
19127         /**
19128          * @event show
19129          * Fires after this menu is displayed
19130          * @param {Roo.menu.Menu} this
19131          */
19132         show : true,
19133         /**
19134          * @event hide
19135          * Fires after this menu is hidden
19136          * @param {Roo.menu.Menu} this
19137          */
19138         hide : true,
19139         /**
19140          * @event click
19141          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19142          * @param {Roo.menu.Menu} this
19143          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19144          * @param {Roo.EventObject} e
19145          */
19146         click : true,
19147         /**
19148          * @event mouseover
19149          * Fires when the mouse is hovering over this menu
19150          * @param {Roo.menu.Menu} this
19151          * @param {Roo.EventObject} e
19152          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19153          */
19154         mouseover : true,
19155         /**
19156          * @event mouseout
19157          * Fires when the mouse exits this menu
19158          * @param {Roo.menu.Menu} this
19159          * @param {Roo.EventObject} e
19160          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19161          */
19162         mouseout : true,
19163         /**
19164          * @event itemclick
19165          * Fires when a menu item contained in this menu is clicked
19166          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19167          * @param {Roo.EventObject} e
19168          */
19169         itemclick: true
19170     });
19171     if (this.registerMenu) {
19172         Roo.menu.MenuMgr.register(this);
19173     }
19174     
19175     var mis = this.items;
19176     this.items = new Roo.util.MixedCollection();
19177     if(mis){
19178         this.add.apply(this, mis);
19179     }
19180 };
19181
19182 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19183     /**
19184      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19185      */
19186     minWidth : 120,
19187     /**
19188      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19189      * for bottom-right shadow (defaults to "sides")
19190      */
19191     shadow : "sides",
19192     /**
19193      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19194      * this menu (defaults to "tl-tr?")
19195      */
19196     subMenuAlign : "tl-tr?",
19197     /**
19198      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19199      * relative to its element of origin (defaults to "tl-bl?")
19200      */
19201     defaultAlign : "tl-bl?",
19202     /**
19203      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19204      */
19205     allowOtherMenus : false,
19206     /**
19207      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19208      */
19209     registerMenu : true,
19210
19211     hidden:true,
19212
19213     // private
19214     render : function(){
19215         if(this.el){
19216             return;
19217         }
19218         var el = this.el = new Roo.Layer({
19219             cls: "x-menu",
19220             shadow:this.shadow,
19221             constrain: false,
19222             parentEl: this.parentEl || document.body,
19223             zindex:15000
19224         });
19225
19226         this.keyNav = new Roo.menu.MenuNav(this);
19227
19228         if(this.plain){
19229             el.addClass("x-menu-plain");
19230         }
19231         if(this.cls){
19232             el.addClass(this.cls);
19233         }
19234         // generic focus element
19235         this.focusEl = el.createChild({
19236             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19237         });
19238         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19239         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19240         
19241         ul.on("mouseover", this.onMouseOver, this);
19242         ul.on("mouseout", this.onMouseOut, this);
19243         this.items.each(function(item){
19244             if (item.hidden) {
19245                 return;
19246             }
19247             
19248             var li = document.createElement("li");
19249             li.className = "x-menu-list-item";
19250             ul.dom.appendChild(li);
19251             item.render(li, this);
19252         }, this);
19253         this.ul = ul;
19254         this.autoWidth();
19255     },
19256
19257     // private
19258     autoWidth : function(){
19259         var el = this.el, ul = this.ul;
19260         if(!el){
19261             return;
19262         }
19263         var w = this.width;
19264         if(w){
19265             el.setWidth(w);
19266         }else if(Roo.isIE){
19267             el.setWidth(this.minWidth);
19268             var t = el.dom.offsetWidth; // force recalc
19269             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19270         }
19271     },
19272
19273     // private
19274     delayAutoWidth : function(){
19275         if(this.rendered){
19276             if(!this.awTask){
19277                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19278             }
19279             this.awTask.delay(20);
19280         }
19281     },
19282
19283     // private
19284     findTargetItem : function(e){
19285         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19286         if(t && t.menuItemId){
19287             return this.items.get(t.menuItemId);
19288         }
19289     },
19290
19291     // private
19292     onClick : function(e){
19293         Roo.log("menu.onClick");
19294         var t = this.findTargetItem(e);
19295         if(!t){
19296             return;
19297         }
19298         Roo.log(e);
19299         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19300             if(t == this.activeItem && t.shouldDeactivate(e)){
19301                 this.activeItem.deactivate();
19302                 delete this.activeItem;
19303                 return;
19304             }
19305             if(t.canActivate){
19306                 this.setActiveItem(t, true);
19307             }
19308             return;
19309             
19310             
19311         }
19312         
19313         t.onClick(e);
19314         this.fireEvent("click", this, t, e);
19315     },
19316
19317     // private
19318     setActiveItem : function(item, autoExpand){
19319         if(item != this.activeItem){
19320             if(this.activeItem){
19321                 this.activeItem.deactivate();
19322             }
19323             this.activeItem = item;
19324             item.activate(autoExpand);
19325         }else if(autoExpand){
19326             item.expandMenu();
19327         }
19328     },
19329
19330     // private
19331     tryActivate : function(start, step){
19332         var items = this.items;
19333         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19334             var item = items.get(i);
19335             if(!item.disabled && item.canActivate){
19336                 this.setActiveItem(item, false);
19337                 return item;
19338             }
19339         }
19340         return false;
19341     },
19342
19343     // private
19344     onMouseOver : function(e){
19345         var t;
19346         if(t = this.findTargetItem(e)){
19347             if(t.canActivate && !t.disabled){
19348                 this.setActiveItem(t, true);
19349             }
19350         }
19351         this.fireEvent("mouseover", this, e, t);
19352     },
19353
19354     // private
19355     onMouseOut : function(e){
19356         var t;
19357         if(t = this.findTargetItem(e)){
19358             if(t == this.activeItem && t.shouldDeactivate(e)){
19359                 this.activeItem.deactivate();
19360                 delete this.activeItem;
19361             }
19362         }
19363         this.fireEvent("mouseout", this, e, t);
19364     },
19365
19366     /**
19367      * Read-only.  Returns true if the menu is currently displayed, else false.
19368      * @type Boolean
19369      */
19370     isVisible : function(){
19371         return this.el && !this.hidden;
19372     },
19373
19374     /**
19375      * Displays this menu relative to another element
19376      * @param {String/HTMLElement/Roo.Element} element The element to align to
19377      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19378      * the element (defaults to this.defaultAlign)
19379      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19380      */
19381     show : function(el, pos, parentMenu){
19382         this.parentMenu = parentMenu;
19383         if(!this.el){
19384             this.render();
19385         }
19386         this.fireEvent("beforeshow", this);
19387         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19388     },
19389
19390     /**
19391      * Displays this menu at a specific xy position
19392      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19393      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19394      */
19395     showAt : function(xy, parentMenu, /* private: */_e){
19396         this.parentMenu = parentMenu;
19397         if(!this.el){
19398             this.render();
19399         }
19400         if(_e !== false){
19401             this.fireEvent("beforeshow", this);
19402             xy = this.el.adjustForConstraints(xy);
19403         }
19404         this.el.setXY(xy);
19405         this.el.show();
19406         this.hidden = false;
19407         this.focus();
19408         this.fireEvent("show", this);
19409     },
19410
19411     focus : function(){
19412         if(!this.hidden){
19413             this.doFocus.defer(50, this);
19414         }
19415     },
19416
19417     doFocus : function(){
19418         if(!this.hidden){
19419             this.focusEl.focus();
19420         }
19421     },
19422
19423     /**
19424      * Hides this menu and optionally all parent menus
19425      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19426      */
19427     hide : function(deep){
19428         if(this.el && this.isVisible()){
19429             this.fireEvent("beforehide", this);
19430             if(this.activeItem){
19431                 this.activeItem.deactivate();
19432                 this.activeItem = null;
19433             }
19434             this.el.hide();
19435             this.hidden = true;
19436             this.fireEvent("hide", this);
19437         }
19438         if(deep === true && this.parentMenu){
19439             this.parentMenu.hide(true);
19440         }
19441     },
19442
19443     /**
19444      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19445      * Any of the following are valid:
19446      * <ul>
19447      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19448      * <li>An HTMLElement object which will be converted to a menu item</li>
19449      * <li>A menu item config object that will be created as a new menu item</li>
19450      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19451      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19452      * </ul>
19453      * Usage:
19454      * <pre><code>
19455 // Create the menu
19456 var menu = new Roo.menu.Menu();
19457
19458 // Create a menu item to add by reference
19459 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19460
19461 // Add a bunch of items at once using different methods.
19462 // Only the last item added will be returned.
19463 var item = menu.add(
19464     menuItem,                // add existing item by ref
19465     'Dynamic Item',          // new TextItem
19466     '-',                     // new separator
19467     { text: 'Config Item' }  // new item by config
19468 );
19469 </code></pre>
19470      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19471      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19472      */
19473     add : function(){
19474         var a = arguments, l = a.length, item;
19475         for(var i = 0; i < l; i++){
19476             var el = a[i];
19477             if ((typeof(el) == "object") && el.xtype && el.xns) {
19478                 el = Roo.factory(el, Roo.menu);
19479             }
19480             
19481             if(el.render){ // some kind of Item
19482                 item = this.addItem(el);
19483             }else if(typeof el == "string"){ // string
19484                 if(el == "separator" || el == "-"){
19485                     item = this.addSeparator();
19486                 }else{
19487                     item = this.addText(el);
19488                 }
19489             }else if(el.tagName || el.el){ // element
19490                 item = this.addElement(el);
19491             }else if(typeof el == "object"){ // must be menu item config?
19492                 item = this.addMenuItem(el);
19493             }
19494         }
19495         return item;
19496     },
19497
19498     /**
19499      * Returns this menu's underlying {@link Roo.Element} object
19500      * @return {Roo.Element} The element
19501      */
19502     getEl : function(){
19503         if(!this.el){
19504             this.render();
19505         }
19506         return this.el;
19507     },
19508
19509     /**
19510      * Adds a separator bar to the menu
19511      * @return {Roo.menu.Item} The menu item that was added
19512      */
19513     addSeparator : function(){
19514         return this.addItem(new Roo.menu.Separator());
19515     },
19516
19517     /**
19518      * Adds an {@link Roo.Element} object to the menu
19519      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19520      * @return {Roo.menu.Item} The menu item that was added
19521      */
19522     addElement : function(el){
19523         return this.addItem(new Roo.menu.BaseItem(el));
19524     },
19525
19526     /**
19527      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19528      * @param {Roo.menu.Item} item The menu item to add
19529      * @return {Roo.menu.Item} The menu item that was added
19530      */
19531     addItem : function(item){
19532         this.items.add(item);
19533         if(this.ul){
19534             var li = document.createElement("li");
19535             li.className = "x-menu-list-item";
19536             this.ul.dom.appendChild(li);
19537             item.render(li, this);
19538             this.delayAutoWidth();
19539         }
19540         return item;
19541     },
19542
19543     /**
19544      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19545      * @param {Object} config A MenuItem config object
19546      * @return {Roo.menu.Item} The menu item that was added
19547      */
19548     addMenuItem : function(config){
19549         if(!(config instanceof Roo.menu.Item)){
19550             if(typeof config.checked == "boolean"){ // must be check menu item config?
19551                 config = new Roo.menu.CheckItem(config);
19552             }else{
19553                 config = new Roo.menu.Item(config);
19554             }
19555         }
19556         return this.addItem(config);
19557     },
19558
19559     /**
19560      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19561      * @param {String} text The text to display in the menu item
19562      * @return {Roo.menu.Item} The menu item that was added
19563      */
19564     addText : function(text){
19565         return this.addItem(new Roo.menu.TextItem({ text : text }));
19566     },
19567
19568     /**
19569      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19570      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19571      * @param {Roo.menu.Item} item The menu item to add
19572      * @return {Roo.menu.Item} The menu item that was added
19573      */
19574     insert : function(index, item){
19575         this.items.insert(index, item);
19576         if(this.ul){
19577             var li = document.createElement("li");
19578             li.className = "x-menu-list-item";
19579             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19580             item.render(li, this);
19581             this.delayAutoWidth();
19582         }
19583         return item;
19584     },
19585
19586     /**
19587      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19588      * @param {Roo.menu.Item} item The menu item to remove
19589      */
19590     remove : function(item){
19591         this.items.removeKey(item.id);
19592         item.destroy();
19593     },
19594
19595     /**
19596      * Removes and destroys all items in the menu
19597      */
19598     removeAll : function(){
19599         var f;
19600         while(f = this.items.first()){
19601             this.remove(f);
19602         }
19603     }
19604 });
19605
19606 // MenuNav is a private utility class used internally by the Menu
19607 Roo.menu.MenuNav = function(menu){
19608     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19609     this.scope = this.menu = menu;
19610 };
19611
19612 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19613     doRelay : function(e, h){
19614         var k = e.getKey();
19615         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19616             this.menu.tryActivate(0, 1);
19617             return false;
19618         }
19619         return h.call(this.scope || this, e, this.menu);
19620     },
19621
19622     up : function(e, m){
19623         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19624             m.tryActivate(m.items.length-1, -1);
19625         }
19626     },
19627
19628     down : function(e, m){
19629         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19630             m.tryActivate(0, 1);
19631         }
19632     },
19633
19634     right : function(e, m){
19635         if(m.activeItem){
19636             m.activeItem.expandMenu(true);
19637         }
19638     },
19639
19640     left : function(e, m){
19641         m.hide();
19642         if(m.parentMenu && m.parentMenu.activeItem){
19643             m.parentMenu.activeItem.activate();
19644         }
19645     },
19646
19647     enter : function(e, m){
19648         if(m.activeItem){
19649             e.stopPropagation();
19650             m.activeItem.onClick(e);
19651             m.fireEvent("click", this, m.activeItem);
19652             return true;
19653         }
19654     }
19655 });/*
19656  * Based on:
19657  * Ext JS Library 1.1.1
19658  * Copyright(c) 2006-2007, Ext JS, LLC.
19659  *
19660  * Originally Released Under LGPL - original licence link has changed is not relivant.
19661  *
19662  * Fork - LGPL
19663  * <script type="text/javascript">
19664  */
19665  
19666 /**
19667  * @class Roo.menu.MenuMgr
19668  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19669  * @singleton
19670  */
19671 Roo.menu.MenuMgr = function(){
19672    var menus, active, groups = {}, attached = false, lastShow = new Date();
19673
19674    // private - called when first menu is created
19675    function init(){
19676        menus = {};
19677        active = new Roo.util.MixedCollection();
19678        Roo.get(document).addKeyListener(27, function(){
19679            if(active.length > 0){
19680                hideAll();
19681            }
19682        });
19683    }
19684
19685    // private
19686    function hideAll(){
19687        if(active && active.length > 0){
19688            var c = active.clone();
19689            c.each(function(m){
19690                m.hide();
19691            });
19692        }
19693    }
19694
19695    // private
19696    function onHide(m){
19697        active.remove(m);
19698        if(active.length < 1){
19699            Roo.get(document).un("mousedown", onMouseDown);
19700            attached = false;
19701        }
19702    }
19703
19704    // private
19705    function onShow(m){
19706        var last = active.last();
19707        lastShow = new Date();
19708        active.add(m);
19709        if(!attached){
19710            Roo.get(document).on("mousedown", onMouseDown);
19711            attached = true;
19712        }
19713        if(m.parentMenu){
19714           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19715           m.parentMenu.activeChild = m;
19716        }else if(last && last.isVisible()){
19717           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19718        }
19719    }
19720
19721    // private
19722    function onBeforeHide(m){
19723        if(m.activeChild){
19724            m.activeChild.hide();
19725        }
19726        if(m.autoHideTimer){
19727            clearTimeout(m.autoHideTimer);
19728            delete m.autoHideTimer;
19729        }
19730    }
19731
19732    // private
19733    function onBeforeShow(m){
19734        var pm = m.parentMenu;
19735        if(!pm && !m.allowOtherMenus){
19736            hideAll();
19737        }else if(pm && pm.activeChild && active != m){
19738            pm.activeChild.hide();
19739        }
19740    }
19741
19742    // private
19743    function onMouseDown(e){
19744        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19745            hideAll();
19746        }
19747    }
19748
19749    // private
19750    function onBeforeCheck(mi, state){
19751        if(state){
19752            var g = groups[mi.group];
19753            for(var i = 0, l = g.length; i < l; i++){
19754                if(g[i] != mi){
19755                    g[i].setChecked(false);
19756                }
19757            }
19758        }
19759    }
19760
19761    return {
19762
19763        /**
19764         * Hides all menus that are currently visible
19765         */
19766        hideAll : function(){
19767             hideAll();  
19768        },
19769
19770        // private
19771        register : function(menu){
19772            if(!menus){
19773                init();
19774            }
19775            menus[menu.id] = menu;
19776            menu.on("beforehide", onBeforeHide);
19777            menu.on("hide", onHide);
19778            menu.on("beforeshow", onBeforeShow);
19779            menu.on("show", onShow);
19780            var g = menu.group;
19781            if(g && menu.events["checkchange"]){
19782                if(!groups[g]){
19783                    groups[g] = [];
19784                }
19785                groups[g].push(menu);
19786                menu.on("checkchange", onCheck);
19787            }
19788        },
19789
19790         /**
19791          * Returns a {@link Roo.menu.Menu} object
19792          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19793          * be used to generate and return a new Menu instance.
19794          */
19795        get : function(menu){
19796            if(typeof menu == "string"){ // menu id
19797                return menus[menu];
19798            }else if(menu.events){  // menu instance
19799                return menu;
19800            }else if(typeof menu.length == 'number'){ // array of menu items?
19801                return new Roo.menu.Menu({items:menu});
19802            }else{ // otherwise, must be a config
19803                return new Roo.menu.Menu(menu);
19804            }
19805        },
19806
19807        // private
19808        unregister : function(menu){
19809            delete menus[menu.id];
19810            menu.un("beforehide", onBeforeHide);
19811            menu.un("hide", onHide);
19812            menu.un("beforeshow", onBeforeShow);
19813            menu.un("show", onShow);
19814            var g = menu.group;
19815            if(g && menu.events["checkchange"]){
19816                groups[g].remove(menu);
19817                menu.un("checkchange", onCheck);
19818            }
19819        },
19820
19821        // private
19822        registerCheckable : function(menuItem){
19823            var g = menuItem.group;
19824            if(g){
19825                if(!groups[g]){
19826                    groups[g] = [];
19827                }
19828                groups[g].push(menuItem);
19829                menuItem.on("beforecheckchange", onBeforeCheck);
19830            }
19831        },
19832
19833        // private
19834        unregisterCheckable : function(menuItem){
19835            var g = menuItem.group;
19836            if(g){
19837                groups[g].remove(menuItem);
19838                menuItem.un("beforecheckchange", onBeforeCheck);
19839            }
19840        }
19841    };
19842 }();/*
19843  * Based on:
19844  * Ext JS Library 1.1.1
19845  * Copyright(c) 2006-2007, Ext JS, LLC.
19846  *
19847  * Originally Released Under LGPL - original licence link has changed is not relivant.
19848  *
19849  * Fork - LGPL
19850  * <script type="text/javascript">
19851  */
19852  
19853
19854 /**
19855  * @class Roo.menu.BaseItem
19856  * @extends Roo.Component
19857  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19858  * management and base configuration options shared by all menu components.
19859  * @constructor
19860  * Creates a new BaseItem
19861  * @param {Object} config Configuration options
19862  */
19863 Roo.menu.BaseItem = function(config){
19864     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19865
19866     this.addEvents({
19867         /**
19868          * @event click
19869          * Fires when this item is clicked
19870          * @param {Roo.menu.BaseItem} this
19871          * @param {Roo.EventObject} e
19872          */
19873         click: true,
19874         /**
19875          * @event activate
19876          * Fires when this item is activated
19877          * @param {Roo.menu.BaseItem} this
19878          */
19879         activate : true,
19880         /**
19881          * @event deactivate
19882          * Fires when this item is deactivated
19883          * @param {Roo.menu.BaseItem} this
19884          */
19885         deactivate : true
19886     });
19887
19888     if(this.handler){
19889         this.on("click", this.handler, this.scope, true);
19890     }
19891 };
19892
19893 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19894     /**
19895      * @cfg {Function} handler
19896      * A function that will handle the click event of this menu item (defaults to undefined)
19897      */
19898     /**
19899      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19900      */
19901     canActivate : false,
19902     
19903      /**
19904      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19905      */
19906     hidden: false,
19907     
19908     /**
19909      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19910      */
19911     activeClass : "x-menu-item-active",
19912     /**
19913      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19914      */
19915     hideOnClick : true,
19916     /**
19917      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19918      */
19919     hideDelay : 100,
19920
19921     // private
19922     ctype: "Roo.menu.BaseItem",
19923
19924     // private
19925     actionMode : "container",
19926
19927     // private
19928     render : function(container, parentMenu){
19929         this.parentMenu = parentMenu;
19930         Roo.menu.BaseItem.superclass.render.call(this, container);
19931         this.container.menuItemId = this.id;
19932     },
19933
19934     // private
19935     onRender : function(container, position){
19936         this.el = Roo.get(this.el);
19937         container.dom.appendChild(this.el.dom);
19938     },
19939
19940     // private
19941     onClick : function(e){
19942         if(!this.disabled && this.fireEvent("click", this, e) !== false
19943                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19944             this.handleClick(e);
19945         }else{
19946             e.stopEvent();
19947         }
19948     },
19949
19950     // private
19951     activate : function(){
19952         if(this.disabled){
19953             return false;
19954         }
19955         var li = this.container;
19956         li.addClass(this.activeClass);
19957         this.region = li.getRegion().adjust(2, 2, -2, -2);
19958         this.fireEvent("activate", this);
19959         return true;
19960     },
19961
19962     // private
19963     deactivate : function(){
19964         this.container.removeClass(this.activeClass);
19965         this.fireEvent("deactivate", this);
19966     },
19967
19968     // private
19969     shouldDeactivate : function(e){
19970         return !this.region || !this.region.contains(e.getPoint());
19971     },
19972
19973     // private
19974     handleClick : function(e){
19975         if(this.hideOnClick){
19976             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19977         }
19978     },
19979
19980     // private
19981     expandMenu : function(autoActivate){
19982         // do nothing
19983     },
19984
19985     // private
19986     hideMenu : function(){
19987         // do nothing
19988     }
19989 });/*
19990  * Based on:
19991  * Ext JS Library 1.1.1
19992  * Copyright(c) 2006-2007, Ext JS, LLC.
19993  *
19994  * Originally Released Under LGPL - original licence link has changed is not relivant.
19995  *
19996  * Fork - LGPL
19997  * <script type="text/javascript">
19998  */
19999  
20000 /**
20001  * @class Roo.menu.Adapter
20002  * @extends Roo.menu.BaseItem
20003  * 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.
20004  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20005  * @constructor
20006  * Creates a new Adapter
20007  * @param {Object} config Configuration options
20008  */
20009 Roo.menu.Adapter = function(component, config){
20010     Roo.menu.Adapter.superclass.constructor.call(this, config);
20011     this.component = component;
20012 };
20013 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20014     // private
20015     canActivate : true,
20016
20017     // private
20018     onRender : function(container, position){
20019         this.component.render(container);
20020         this.el = this.component.getEl();
20021     },
20022
20023     // private
20024     activate : function(){
20025         if(this.disabled){
20026             return false;
20027         }
20028         this.component.focus();
20029         this.fireEvent("activate", this);
20030         return true;
20031     },
20032
20033     // private
20034     deactivate : function(){
20035         this.fireEvent("deactivate", this);
20036     },
20037
20038     // private
20039     disable : function(){
20040         this.component.disable();
20041         Roo.menu.Adapter.superclass.disable.call(this);
20042     },
20043
20044     // private
20045     enable : function(){
20046         this.component.enable();
20047         Roo.menu.Adapter.superclass.enable.call(this);
20048     }
20049 });/*
20050  * Based on:
20051  * Ext JS Library 1.1.1
20052  * Copyright(c) 2006-2007, Ext JS, LLC.
20053  *
20054  * Originally Released Under LGPL - original licence link has changed is not relivant.
20055  *
20056  * Fork - LGPL
20057  * <script type="text/javascript">
20058  */
20059
20060 /**
20061  * @class Roo.menu.TextItem
20062  * @extends Roo.menu.BaseItem
20063  * Adds a static text string to a menu, usually used as either a heading or group separator.
20064  * Note: old style constructor with text is still supported.
20065  * 
20066  * @constructor
20067  * Creates a new TextItem
20068  * @param {Object} cfg Configuration
20069  */
20070 Roo.menu.TextItem = function(cfg){
20071     if (typeof(cfg) == 'string') {
20072         this.text = cfg;
20073     } else {
20074         Roo.apply(this,cfg);
20075     }
20076     
20077     Roo.menu.TextItem.superclass.constructor.call(this);
20078 };
20079
20080 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20081     /**
20082      * @cfg {Boolean} text Text to show on item.
20083      */
20084     text : '',
20085     
20086     /**
20087      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20088      */
20089     hideOnClick : false,
20090     /**
20091      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20092      */
20093     itemCls : "x-menu-text",
20094
20095     // private
20096     onRender : function(){
20097         var s = document.createElement("span");
20098         s.className = this.itemCls;
20099         s.innerHTML = this.text;
20100         this.el = s;
20101         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20102     }
20103 });/*
20104  * Based on:
20105  * Ext JS Library 1.1.1
20106  * Copyright(c) 2006-2007, Ext JS, LLC.
20107  *
20108  * Originally Released Under LGPL - original licence link has changed is not relivant.
20109  *
20110  * Fork - LGPL
20111  * <script type="text/javascript">
20112  */
20113
20114 /**
20115  * @class Roo.menu.Separator
20116  * @extends Roo.menu.BaseItem
20117  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20118  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20119  * @constructor
20120  * @param {Object} config Configuration options
20121  */
20122 Roo.menu.Separator = function(config){
20123     Roo.menu.Separator.superclass.constructor.call(this, config);
20124 };
20125
20126 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20127     /**
20128      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20129      */
20130     itemCls : "x-menu-sep",
20131     /**
20132      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20133      */
20134     hideOnClick : false,
20135
20136     // private
20137     onRender : function(li){
20138         var s = document.createElement("span");
20139         s.className = this.itemCls;
20140         s.innerHTML = "&#160;";
20141         this.el = s;
20142         li.addClass("x-menu-sep-li");
20143         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20144     }
20145 });/*
20146  * Based on:
20147  * Ext JS Library 1.1.1
20148  * Copyright(c) 2006-2007, Ext JS, LLC.
20149  *
20150  * Originally Released Under LGPL - original licence link has changed is not relivant.
20151  *
20152  * Fork - LGPL
20153  * <script type="text/javascript">
20154  */
20155 /**
20156  * @class Roo.menu.Item
20157  * @extends Roo.menu.BaseItem
20158  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20159  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20160  * activation and click handling.
20161  * @constructor
20162  * Creates a new Item
20163  * @param {Object} config Configuration options
20164  */
20165 Roo.menu.Item = function(config){
20166     Roo.menu.Item.superclass.constructor.call(this, config);
20167     if(this.menu){
20168         this.menu = Roo.menu.MenuMgr.get(this.menu);
20169     }
20170 };
20171 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20172     
20173     /**
20174      * @cfg {String} text
20175      * The text to show on the menu item.
20176      */
20177     text: '',
20178      /**
20179      * @cfg {String} HTML to render in menu
20180      * The text to show on the menu item (HTML version).
20181      */
20182     html: '',
20183     /**
20184      * @cfg {String} icon
20185      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20186      */
20187     icon: undefined,
20188     /**
20189      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20190      */
20191     itemCls : "x-menu-item",
20192     /**
20193      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20194      */
20195     canActivate : true,
20196     /**
20197      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20198      */
20199     showDelay: 200,
20200     // doc'd in BaseItem
20201     hideDelay: 200,
20202
20203     // private
20204     ctype: "Roo.menu.Item",
20205     
20206     // private
20207     onRender : function(container, position){
20208         var el = document.createElement("a");
20209         el.hideFocus = true;
20210         el.unselectable = "on";
20211         el.href = this.href || "#";
20212         if(this.hrefTarget){
20213             el.target = this.hrefTarget;
20214         }
20215         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20216         
20217         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20218         
20219         el.innerHTML = String.format(
20220                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20221                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20222         this.el = el;
20223         Roo.menu.Item.superclass.onRender.call(this, container, position);
20224     },
20225
20226     /**
20227      * Sets the text to display in this menu item
20228      * @param {String} text The text to display
20229      * @param {Boolean} isHTML true to indicate text is pure html.
20230      */
20231     setText : function(text, isHTML){
20232         if (isHTML) {
20233             this.html = text;
20234         } else {
20235             this.text = text;
20236             this.html = '';
20237         }
20238         if(this.rendered){
20239             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20240      
20241             this.el.update(String.format(
20242                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20243                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20244             this.parentMenu.autoWidth();
20245         }
20246     },
20247
20248     // private
20249     handleClick : function(e){
20250         if(!this.href){ // if no link defined, stop the event automatically
20251             e.stopEvent();
20252         }
20253         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20254     },
20255
20256     // private
20257     activate : function(autoExpand){
20258         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20259             this.focus();
20260             if(autoExpand){
20261                 this.expandMenu();
20262             }
20263         }
20264         return true;
20265     },
20266
20267     // private
20268     shouldDeactivate : function(e){
20269         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20270             if(this.menu && this.menu.isVisible()){
20271                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20272             }
20273             return true;
20274         }
20275         return false;
20276     },
20277
20278     // private
20279     deactivate : function(){
20280         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20281         this.hideMenu();
20282     },
20283
20284     // private
20285     expandMenu : function(autoActivate){
20286         if(!this.disabled && this.menu){
20287             clearTimeout(this.hideTimer);
20288             delete this.hideTimer;
20289             if(!this.menu.isVisible() && !this.showTimer){
20290                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20291             }else if (this.menu.isVisible() && autoActivate){
20292                 this.menu.tryActivate(0, 1);
20293             }
20294         }
20295     },
20296
20297     // private
20298     deferExpand : function(autoActivate){
20299         delete this.showTimer;
20300         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20301         if(autoActivate){
20302             this.menu.tryActivate(0, 1);
20303         }
20304     },
20305
20306     // private
20307     hideMenu : function(){
20308         clearTimeout(this.showTimer);
20309         delete this.showTimer;
20310         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20311             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20312         }
20313     },
20314
20315     // private
20316     deferHide : function(){
20317         delete this.hideTimer;
20318         this.menu.hide();
20319     }
20320 });/*
20321  * Based on:
20322  * Ext JS Library 1.1.1
20323  * Copyright(c) 2006-2007, Ext JS, LLC.
20324  *
20325  * Originally Released Under LGPL - original licence link has changed is not relivant.
20326  *
20327  * Fork - LGPL
20328  * <script type="text/javascript">
20329  */
20330  
20331 /**
20332  * @class Roo.menu.CheckItem
20333  * @extends Roo.menu.Item
20334  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20335  * @constructor
20336  * Creates a new CheckItem
20337  * @param {Object} config Configuration options
20338  */
20339 Roo.menu.CheckItem = function(config){
20340     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20341     this.addEvents({
20342         /**
20343          * @event beforecheckchange
20344          * Fires before the checked value is set, providing an opportunity to cancel if needed
20345          * @param {Roo.menu.CheckItem} this
20346          * @param {Boolean} checked The new checked value that will be set
20347          */
20348         "beforecheckchange" : true,
20349         /**
20350          * @event checkchange
20351          * Fires after the checked value has been set
20352          * @param {Roo.menu.CheckItem} this
20353          * @param {Boolean} checked The checked value that was set
20354          */
20355         "checkchange" : true
20356     });
20357     if(this.checkHandler){
20358         this.on('checkchange', this.checkHandler, this.scope);
20359     }
20360 };
20361 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20362     /**
20363      * @cfg {String} group
20364      * All check items with the same group name will automatically be grouped into a single-select
20365      * radio button group (defaults to '')
20366      */
20367     /**
20368      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20369      */
20370     itemCls : "x-menu-item x-menu-check-item",
20371     /**
20372      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20373      */
20374     groupClass : "x-menu-group-item",
20375
20376     /**
20377      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20378      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20379      * initialized with checked = true will be rendered as checked.
20380      */
20381     checked: false,
20382
20383     // private
20384     ctype: "Roo.menu.CheckItem",
20385
20386     // private
20387     onRender : function(c){
20388         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20389         if(this.group){
20390             this.el.addClass(this.groupClass);
20391         }
20392         Roo.menu.MenuMgr.registerCheckable(this);
20393         if(this.checked){
20394             this.checked = false;
20395             this.setChecked(true, true);
20396         }
20397     },
20398
20399     // private
20400     destroy : function(){
20401         if(this.rendered){
20402             Roo.menu.MenuMgr.unregisterCheckable(this);
20403         }
20404         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20405     },
20406
20407     /**
20408      * Set the checked state of this item
20409      * @param {Boolean} checked The new checked value
20410      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20411      */
20412     setChecked : function(state, suppressEvent){
20413         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20414             if(this.container){
20415                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20416             }
20417             this.checked = state;
20418             if(suppressEvent !== true){
20419                 this.fireEvent("checkchange", this, state);
20420             }
20421         }
20422     },
20423
20424     // private
20425     handleClick : function(e){
20426        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20427            this.setChecked(!this.checked);
20428        }
20429        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20430     }
20431 });/*
20432  * Based on:
20433  * Ext JS Library 1.1.1
20434  * Copyright(c) 2006-2007, Ext JS, LLC.
20435  *
20436  * Originally Released Under LGPL - original licence link has changed is not relivant.
20437  *
20438  * Fork - LGPL
20439  * <script type="text/javascript">
20440  */
20441  
20442 /**
20443  * @class Roo.menu.DateItem
20444  * @extends Roo.menu.Adapter
20445  * A menu item that wraps the {@link Roo.DatPicker} component.
20446  * @constructor
20447  * Creates a new DateItem
20448  * @param {Object} config Configuration options
20449  */
20450 Roo.menu.DateItem = function(config){
20451     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20452     /** The Roo.DatePicker object @type Roo.DatePicker */
20453     this.picker = this.component;
20454     this.addEvents({select: true});
20455     
20456     this.picker.on("render", function(picker){
20457         picker.getEl().swallowEvent("click");
20458         picker.container.addClass("x-menu-date-item");
20459     });
20460
20461     this.picker.on("select", this.onSelect, this);
20462 };
20463
20464 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20465     // private
20466     onSelect : function(picker, date){
20467         this.fireEvent("select", this, date, picker);
20468         Roo.menu.DateItem.superclass.handleClick.call(this);
20469     }
20470 });/*
20471  * Based on:
20472  * Ext JS Library 1.1.1
20473  * Copyright(c) 2006-2007, Ext JS, LLC.
20474  *
20475  * Originally Released Under LGPL - original licence link has changed is not relivant.
20476  *
20477  * Fork - LGPL
20478  * <script type="text/javascript">
20479  */
20480  
20481 /**
20482  * @class Roo.menu.ColorItem
20483  * @extends Roo.menu.Adapter
20484  * A menu item that wraps the {@link Roo.ColorPalette} component.
20485  * @constructor
20486  * Creates a new ColorItem
20487  * @param {Object} config Configuration options
20488  */
20489 Roo.menu.ColorItem = function(config){
20490     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20491     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20492     this.palette = this.component;
20493     this.relayEvents(this.palette, ["select"]);
20494     if(this.selectHandler){
20495         this.on('select', this.selectHandler, this.scope);
20496     }
20497 };
20498 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20499  * Based on:
20500  * Ext JS Library 1.1.1
20501  * Copyright(c) 2006-2007, Ext JS, LLC.
20502  *
20503  * Originally Released Under LGPL - original licence link has changed is not relivant.
20504  *
20505  * Fork - LGPL
20506  * <script type="text/javascript">
20507  */
20508  
20509
20510 /**
20511  * @class Roo.menu.DateMenu
20512  * @extends Roo.menu.Menu
20513  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20514  * @constructor
20515  * Creates a new DateMenu
20516  * @param {Object} config Configuration options
20517  */
20518 Roo.menu.DateMenu = function(config){
20519     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20520     this.plain = true;
20521     var di = new Roo.menu.DateItem(config);
20522     this.add(di);
20523     /**
20524      * The {@link Roo.DatePicker} instance for this DateMenu
20525      * @type DatePicker
20526      */
20527     this.picker = di.picker;
20528     /**
20529      * @event select
20530      * @param {DatePicker} picker
20531      * @param {Date} date
20532      */
20533     this.relayEvents(di, ["select"]);
20534     this.on('beforeshow', function(){
20535         if(this.picker){
20536             this.picker.hideMonthPicker(false);
20537         }
20538     }, this);
20539 };
20540 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20541     cls:'x-date-menu'
20542 });/*
20543  * Based on:
20544  * Ext JS Library 1.1.1
20545  * Copyright(c) 2006-2007, Ext JS, LLC.
20546  *
20547  * Originally Released Under LGPL - original licence link has changed is not relivant.
20548  *
20549  * Fork - LGPL
20550  * <script type="text/javascript">
20551  */
20552  
20553
20554 /**
20555  * @class Roo.menu.ColorMenu
20556  * @extends Roo.menu.Menu
20557  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20558  * @constructor
20559  * Creates a new ColorMenu
20560  * @param {Object} config Configuration options
20561  */
20562 Roo.menu.ColorMenu = function(config){
20563     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20564     this.plain = true;
20565     var ci = new Roo.menu.ColorItem(config);
20566     this.add(ci);
20567     /**
20568      * The {@link Roo.ColorPalette} instance for this ColorMenu
20569      * @type ColorPalette
20570      */
20571     this.palette = ci.palette;
20572     /**
20573      * @event select
20574      * @param {ColorPalette} palette
20575      * @param {String} color
20576      */
20577     this.relayEvents(ci, ["select"]);
20578 };
20579 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20580  * Based on:
20581  * Ext JS Library 1.1.1
20582  * Copyright(c) 2006-2007, Ext JS, LLC.
20583  *
20584  * Originally Released Under LGPL - original licence link has changed is not relivant.
20585  *
20586  * Fork - LGPL
20587  * <script type="text/javascript">
20588  */
20589  
20590 /**
20591  * @class Roo.form.Field
20592  * @extends Roo.BoxComponent
20593  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20594  * @constructor
20595  * Creates a new Field
20596  * @param {Object} config Configuration options
20597  */
20598 Roo.form.Field = function(config){
20599     Roo.form.Field.superclass.constructor.call(this, config);
20600 };
20601
20602 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20603     /**
20604      * @cfg {String} fieldLabel Label to use when rendering a form.
20605      */
20606        /**
20607      * @cfg {String} qtip Mouse over tip
20608      */
20609      
20610     /**
20611      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20612      */
20613     invalidClass : "x-form-invalid",
20614     /**
20615      * @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")
20616      */
20617     invalidText : "The value in this field is invalid",
20618     /**
20619      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20620      */
20621     focusClass : "x-form-focus",
20622     /**
20623      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20624       automatic validation (defaults to "keyup").
20625      */
20626     validationEvent : "keyup",
20627     /**
20628      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20629      */
20630     validateOnBlur : true,
20631     /**
20632      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20633      */
20634     validationDelay : 250,
20635     /**
20636      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20637      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20638      */
20639     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20640     /**
20641      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20642      */
20643     fieldClass : "x-form-field",
20644     /**
20645      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20646      *<pre>
20647 Value         Description
20648 -----------   ----------------------------------------------------------------------
20649 qtip          Display a quick tip when the user hovers over the field
20650 title         Display a default browser title attribute popup
20651 under         Add a block div beneath the field containing the error text
20652 side          Add an error icon to the right of the field with a popup on hover
20653 [element id]  Add the error text directly to the innerHTML of the specified element
20654 </pre>
20655      */
20656     msgTarget : 'qtip',
20657     /**
20658      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20659      */
20660     msgFx : 'normal',
20661
20662     /**
20663      * @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.
20664      */
20665     readOnly : false,
20666
20667     /**
20668      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20669      */
20670     disabled : false,
20671
20672     /**
20673      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20674      */
20675     inputType : undefined,
20676     
20677     /**
20678      * @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).
20679          */
20680         tabIndex : undefined,
20681         
20682     // private
20683     isFormField : true,
20684
20685     // private
20686     hasFocus : false,
20687     /**
20688      * @property {Roo.Element} fieldEl
20689      * Element Containing the rendered Field (with label etc.)
20690      */
20691     /**
20692      * @cfg {Mixed} value A value to initialize this field with.
20693      */
20694     value : undefined,
20695
20696     /**
20697      * @cfg {String} name The field's HTML name attribute.
20698      */
20699     /**
20700      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20701      */
20702
20703         // private ??
20704         initComponent : function(){
20705         Roo.form.Field.superclass.initComponent.call(this);
20706         this.addEvents({
20707             /**
20708              * @event focus
20709              * Fires when this field receives input focus.
20710              * @param {Roo.form.Field} this
20711              */
20712             focus : true,
20713             /**
20714              * @event blur
20715              * Fires when this field loses input focus.
20716              * @param {Roo.form.Field} this
20717              */
20718             blur : true,
20719             /**
20720              * @event specialkey
20721              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20722              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20723              * @param {Roo.form.Field} this
20724              * @param {Roo.EventObject} e The event object
20725              */
20726             specialkey : true,
20727             /**
20728              * @event change
20729              * Fires just before the field blurs if the field value has changed.
20730              * @param {Roo.form.Field} this
20731              * @param {Mixed} newValue The new value
20732              * @param {Mixed} oldValue The original value
20733              */
20734             change : true,
20735             /**
20736              * @event invalid
20737              * Fires after the field has been marked as invalid.
20738              * @param {Roo.form.Field} this
20739              * @param {String} msg The validation message
20740              */
20741             invalid : true,
20742             /**
20743              * @event valid
20744              * Fires after the field has been validated with no errors.
20745              * @param {Roo.form.Field} this
20746              */
20747             valid : true,
20748              /**
20749              * @event keyup
20750              * Fires after the key up
20751              * @param {Roo.form.Field} this
20752              * @param {Roo.EventObject}  e The event Object
20753              */
20754             keyup : true
20755         });
20756     },
20757
20758     /**
20759      * Returns the name attribute of the field if available
20760      * @return {String} name The field name
20761      */
20762     getName: function(){
20763          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20764     },
20765
20766     // private
20767     onRender : function(ct, position){
20768         Roo.form.Field.superclass.onRender.call(this, ct, position);
20769         if(!this.el){
20770             var cfg = this.getAutoCreate();
20771             if(!cfg.name){
20772                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20773             }
20774             if (!cfg.name.length) {
20775                 delete cfg.name;
20776             }
20777             if(this.inputType){
20778                 cfg.type = this.inputType;
20779             }
20780             this.el = ct.createChild(cfg, position);
20781         }
20782         var type = this.el.dom.type;
20783         if(type){
20784             if(type == 'password'){
20785                 type = 'text';
20786             }
20787             this.el.addClass('x-form-'+type);
20788         }
20789         if(this.readOnly){
20790             this.el.dom.readOnly = true;
20791         }
20792         if(this.tabIndex !== undefined){
20793             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20794         }
20795
20796         this.el.addClass([this.fieldClass, this.cls]);
20797         this.initValue();
20798     },
20799
20800     /**
20801      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20802      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20803      * @return {Roo.form.Field} this
20804      */
20805     applyTo : function(target){
20806         this.allowDomMove = false;
20807         this.el = Roo.get(target);
20808         this.render(this.el.dom.parentNode);
20809         return this;
20810     },
20811
20812     // private
20813     initValue : function(){
20814         if(this.value !== undefined){
20815             this.setValue(this.value);
20816         }else if(this.el.dom.value.length > 0){
20817             this.setValue(this.el.dom.value);
20818         }
20819     },
20820
20821     /**
20822      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20823      */
20824     isDirty : function() {
20825         if(this.disabled) {
20826             return false;
20827         }
20828         return String(this.getValue()) !== String(this.originalValue);
20829     },
20830
20831     // private
20832     afterRender : function(){
20833         Roo.form.Field.superclass.afterRender.call(this);
20834         this.initEvents();
20835     },
20836
20837     // private
20838     fireKey : function(e){
20839         //Roo.log('field ' + e.getKey());
20840         if(e.isNavKeyPress()){
20841             this.fireEvent("specialkey", this, e);
20842         }
20843     },
20844
20845     /**
20846      * Resets the current field value to the originally loaded value and clears any validation messages
20847      */
20848     reset : function(){
20849         this.setValue(this.resetValue);
20850         this.clearInvalid();
20851     },
20852
20853     // private
20854     initEvents : function(){
20855         // safari killled keypress - so keydown is now used..
20856         this.el.on("keydown" , this.fireKey,  this);
20857         this.el.on("focus", this.onFocus,  this);
20858         this.el.on("blur", this.onBlur,  this);
20859         this.el.relayEvent('keyup', this);
20860
20861         // reference to original value for reset
20862         this.originalValue = this.getValue();
20863         this.resetValue =  this.getValue();
20864     },
20865
20866     // private
20867     onFocus : function(){
20868         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20869             this.el.addClass(this.focusClass);
20870         }
20871         if(!this.hasFocus){
20872             this.hasFocus = true;
20873             this.startValue = this.getValue();
20874             this.fireEvent("focus", this);
20875         }
20876     },
20877
20878     beforeBlur : Roo.emptyFn,
20879
20880     // private
20881     onBlur : function(){
20882         this.beforeBlur();
20883         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20884             this.el.removeClass(this.focusClass);
20885         }
20886         this.hasFocus = false;
20887         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20888             this.validate();
20889         }
20890         var v = this.getValue();
20891         if(String(v) !== String(this.startValue)){
20892             this.fireEvent('change', this, v, this.startValue);
20893         }
20894         this.fireEvent("blur", this);
20895     },
20896
20897     /**
20898      * Returns whether or not the field value is currently valid
20899      * @param {Boolean} preventMark True to disable marking the field invalid
20900      * @return {Boolean} True if the value is valid, else false
20901      */
20902     isValid : function(preventMark){
20903         if(this.disabled){
20904             return true;
20905         }
20906         var restore = this.preventMark;
20907         this.preventMark = preventMark === true;
20908         var v = this.validateValue(this.processValue(this.getRawValue()));
20909         this.preventMark = restore;
20910         return v;
20911     },
20912
20913     /**
20914      * Validates the field value
20915      * @return {Boolean} True if the value is valid, else false
20916      */
20917     validate : function(){
20918         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20919             this.clearInvalid();
20920             return true;
20921         }
20922         return false;
20923     },
20924
20925     processValue : function(value){
20926         return value;
20927     },
20928
20929     // private
20930     // Subclasses should provide the validation implementation by overriding this
20931     validateValue : function(value){
20932         return true;
20933     },
20934
20935     /**
20936      * Mark this field as invalid
20937      * @param {String} msg The validation message
20938      */
20939     markInvalid : function(msg){
20940         if(!this.rendered || this.preventMark){ // not rendered
20941             return;
20942         }
20943         
20944         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20945         
20946         obj.el.addClass(this.invalidClass);
20947         msg = msg || this.invalidText;
20948         switch(this.msgTarget){
20949             case 'qtip':
20950                 obj.el.dom.qtip = msg;
20951                 obj.el.dom.qclass = 'x-form-invalid-tip';
20952                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20953                     Roo.QuickTips.enable();
20954                 }
20955                 break;
20956             case 'title':
20957                 this.el.dom.title = msg;
20958                 break;
20959             case 'under':
20960                 if(!this.errorEl){
20961                     var elp = this.el.findParent('.x-form-element', 5, true);
20962                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20963                     this.errorEl.setWidth(elp.getWidth(true)-20);
20964                 }
20965                 this.errorEl.update(msg);
20966                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20967                 break;
20968             case 'side':
20969                 if(!this.errorIcon){
20970                     var elp = this.el.findParent('.x-form-element', 5, true);
20971                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20972                 }
20973                 this.alignErrorIcon();
20974                 this.errorIcon.dom.qtip = msg;
20975                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20976                 this.errorIcon.show();
20977                 this.on('resize', this.alignErrorIcon, this);
20978                 break;
20979             default:
20980                 var t = Roo.getDom(this.msgTarget);
20981                 t.innerHTML = msg;
20982                 t.style.display = this.msgDisplay;
20983                 break;
20984         }
20985         this.fireEvent('invalid', this, msg);
20986     },
20987
20988     // private
20989     alignErrorIcon : function(){
20990         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20991     },
20992
20993     /**
20994      * Clear any invalid styles/messages for this field
20995      */
20996     clearInvalid : function(){
20997         if(!this.rendered || this.preventMark){ // not rendered
20998             return;
20999         }
21000         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
21001         
21002         obj.el.removeClass(this.invalidClass);
21003         switch(this.msgTarget){
21004             case 'qtip':
21005                 obj.el.dom.qtip = '';
21006                 break;
21007             case 'title':
21008                 this.el.dom.title = '';
21009                 break;
21010             case 'under':
21011                 if(this.errorEl){
21012                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21013                 }
21014                 break;
21015             case 'side':
21016                 if(this.errorIcon){
21017                     this.errorIcon.dom.qtip = '';
21018                     this.errorIcon.hide();
21019                     this.un('resize', this.alignErrorIcon, this);
21020                 }
21021                 break;
21022             default:
21023                 var t = Roo.getDom(this.msgTarget);
21024                 t.innerHTML = '';
21025                 t.style.display = 'none';
21026                 break;
21027         }
21028         this.fireEvent('valid', this);
21029     },
21030
21031     /**
21032      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21033      * @return {Mixed} value The field value
21034      */
21035     getRawValue : function(){
21036         var v = this.el.getValue();
21037         
21038         return v;
21039     },
21040
21041     /**
21042      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21043      * @return {Mixed} value The field value
21044      */
21045     getValue : function(){
21046         var v = this.el.getValue();
21047          
21048         return v;
21049     },
21050
21051     /**
21052      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21053      * @param {Mixed} value The value to set
21054      */
21055     setRawValue : function(v){
21056         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21057     },
21058
21059     /**
21060      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21061      * @param {Mixed} value The value to set
21062      */
21063     setValue : function(v){
21064         this.value = v;
21065         if(this.rendered){
21066             this.el.dom.value = (v === null || v === undefined ? '' : v);
21067              this.validate();
21068         }
21069     },
21070
21071     adjustSize : function(w, h){
21072         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21073         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21074         return s;
21075     },
21076
21077     adjustWidth : function(tag, w){
21078         tag = tag.toLowerCase();
21079         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21080             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21081                 if(tag == 'input'){
21082                     return w + 2;
21083                 }
21084                 if(tag == 'textarea'){
21085                     return w-2;
21086                 }
21087             }else if(Roo.isOpera){
21088                 if(tag == 'input'){
21089                     return w + 2;
21090                 }
21091                 if(tag == 'textarea'){
21092                     return w-2;
21093                 }
21094             }
21095         }
21096         return w;
21097     }
21098 });
21099
21100
21101 // anything other than normal should be considered experimental
21102 Roo.form.Field.msgFx = {
21103     normal : {
21104         show: function(msgEl, f){
21105             msgEl.setDisplayed('block');
21106         },
21107
21108         hide : function(msgEl, f){
21109             msgEl.setDisplayed(false).update('');
21110         }
21111     },
21112
21113     slide : {
21114         show: function(msgEl, f){
21115             msgEl.slideIn('t', {stopFx:true});
21116         },
21117
21118         hide : function(msgEl, f){
21119             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21120         }
21121     },
21122
21123     slideRight : {
21124         show: function(msgEl, f){
21125             msgEl.fixDisplay();
21126             msgEl.alignTo(f.el, 'tl-tr');
21127             msgEl.slideIn('l', {stopFx:true});
21128         },
21129
21130         hide : function(msgEl, f){
21131             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21132         }
21133     }
21134 };/*
21135  * Based on:
21136  * Ext JS Library 1.1.1
21137  * Copyright(c) 2006-2007, Ext JS, LLC.
21138  *
21139  * Originally Released Under LGPL - original licence link has changed is not relivant.
21140  *
21141  * Fork - LGPL
21142  * <script type="text/javascript">
21143  */
21144  
21145
21146 /**
21147  * @class Roo.form.TextField
21148  * @extends Roo.form.Field
21149  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21150  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21151  * @constructor
21152  * Creates a new TextField
21153  * @param {Object} config Configuration options
21154  */
21155 Roo.form.TextField = function(config){
21156     Roo.form.TextField.superclass.constructor.call(this, config);
21157     this.addEvents({
21158         /**
21159          * @event autosize
21160          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21161          * according to the default logic, but this event provides a hook for the developer to apply additional
21162          * logic at runtime to resize the field if needed.
21163              * @param {Roo.form.Field} this This text field
21164              * @param {Number} width The new field width
21165              */
21166         autosize : true
21167     });
21168 };
21169
21170 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21171     /**
21172      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21173      */
21174     grow : false,
21175     /**
21176      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21177      */
21178     growMin : 30,
21179     /**
21180      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21181      */
21182     growMax : 800,
21183     /**
21184      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21185      */
21186     vtype : null,
21187     /**
21188      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21189      */
21190     maskRe : null,
21191     /**
21192      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21193      */
21194     disableKeyFilter : false,
21195     /**
21196      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21197      */
21198     allowBlank : true,
21199     /**
21200      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21201      */
21202     minLength : 0,
21203     /**
21204      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21205      */
21206     maxLength : Number.MAX_VALUE,
21207     /**
21208      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21209      */
21210     minLengthText : "The minimum length for this field is {0}",
21211     /**
21212      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21213      */
21214     maxLengthText : "The maximum length for this field is {0}",
21215     /**
21216      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21217      */
21218     selectOnFocus : false,
21219     /**
21220      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21221      */
21222     blankText : "This field is required",
21223     /**
21224      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21225      * If available, this function will be called only after the basic validators all return true, and will be passed the
21226      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21227      */
21228     validator : null,
21229     /**
21230      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21231      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21232      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21233      */
21234     regex : null,
21235     /**
21236      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21237      */
21238     regexText : "",
21239     /**
21240      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21241      */
21242     emptyText : null,
21243    
21244
21245     // private
21246     initEvents : function()
21247     {
21248         if (this.emptyText) {
21249             this.el.attr('placeholder', this.emptyText);
21250         }
21251         
21252         Roo.form.TextField.superclass.initEvents.call(this);
21253         if(this.validationEvent == 'keyup'){
21254             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21255             this.el.on('keyup', this.filterValidation, this);
21256         }
21257         else if(this.validationEvent !== false){
21258             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21259         }
21260         
21261         if(this.selectOnFocus){
21262             this.on("focus", this.preFocus, this);
21263             
21264         }
21265         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21266             this.el.on("keypress", this.filterKeys, this);
21267         }
21268         if(this.grow){
21269             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21270             this.el.on("click", this.autoSize,  this);
21271         }
21272         if(this.el.is('input[type=password]') && Roo.isSafari){
21273             this.el.on('keydown', this.SafariOnKeyDown, this);
21274         }
21275     },
21276
21277     processValue : function(value){
21278         if(this.stripCharsRe){
21279             var newValue = value.replace(this.stripCharsRe, '');
21280             if(newValue !== value){
21281                 this.setRawValue(newValue);
21282                 return newValue;
21283             }
21284         }
21285         return value;
21286     },
21287
21288     filterValidation : function(e){
21289         if(!e.isNavKeyPress()){
21290             this.validationTask.delay(this.validationDelay);
21291         }
21292     },
21293
21294     // private
21295     onKeyUp : function(e){
21296         if(!e.isNavKeyPress()){
21297             this.autoSize();
21298         }
21299     },
21300
21301     /**
21302      * Resets the current field value to the originally-loaded value and clears any validation messages.
21303      *  
21304      */
21305     reset : function(){
21306         Roo.form.TextField.superclass.reset.call(this);
21307        
21308     },
21309
21310     
21311     // private
21312     preFocus : function(){
21313         
21314         if(this.selectOnFocus){
21315             this.el.dom.select();
21316         }
21317     },
21318
21319     
21320     // private
21321     filterKeys : function(e){
21322         var k = e.getKey();
21323         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21324             return;
21325         }
21326         var c = e.getCharCode(), cc = String.fromCharCode(c);
21327         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21328             return;
21329         }
21330         if(!this.maskRe.test(cc)){
21331             e.stopEvent();
21332         }
21333     },
21334
21335     setValue : function(v){
21336         
21337         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21338         
21339         this.autoSize();
21340     },
21341
21342     /**
21343      * Validates a value according to the field's validation rules and marks the field as invalid
21344      * if the validation fails
21345      * @param {Mixed} value The value to validate
21346      * @return {Boolean} True if the value is valid, else false
21347      */
21348     validateValue : function(value){
21349         if(value.length < 1)  { // if it's blank
21350              if(this.allowBlank){
21351                 this.clearInvalid();
21352                 return true;
21353              }else{
21354                 this.markInvalid(this.blankText);
21355                 return false;
21356              }
21357         }
21358         if(value.length < this.minLength){
21359             this.markInvalid(String.format(this.minLengthText, this.minLength));
21360             return false;
21361         }
21362         if(value.length > this.maxLength){
21363             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21364             return false;
21365         }
21366         if(this.vtype){
21367             var vt = Roo.form.VTypes;
21368             if(!vt[this.vtype](value, this)){
21369                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21370                 return false;
21371             }
21372         }
21373         if(typeof this.validator == "function"){
21374             var msg = this.validator(value);
21375             if(msg !== true){
21376                 this.markInvalid(msg);
21377                 return false;
21378             }
21379         }
21380         if(this.regex && !this.regex.test(value)){
21381             this.markInvalid(this.regexText);
21382             return false;
21383         }
21384         return true;
21385     },
21386
21387     /**
21388      * Selects text in this field
21389      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21390      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21391      */
21392     selectText : function(start, end){
21393         var v = this.getRawValue();
21394         if(v.length > 0){
21395             start = start === undefined ? 0 : start;
21396             end = end === undefined ? v.length : end;
21397             var d = this.el.dom;
21398             if(d.setSelectionRange){
21399                 d.setSelectionRange(start, end);
21400             }else if(d.createTextRange){
21401                 var range = d.createTextRange();
21402                 range.moveStart("character", start);
21403                 range.moveEnd("character", v.length-end);
21404                 range.select();
21405             }
21406         }
21407     },
21408
21409     /**
21410      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21411      * This only takes effect if grow = true, and fires the autosize event.
21412      */
21413     autoSize : function(){
21414         if(!this.grow || !this.rendered){
21415             return;
21416         }
21417         if(!this.metrics){
21418             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21419         }
21420         var el = this.el;
21421         var v = el.dom.value;
21422         var d = document.createElement('div');
21423         d.appendChild(document.createTextNode(v));
21424         v = d.innerHTML;
21425         d = null;
21426         v += "&#160;";
21427         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21428         this.el.setWidth(w);
21429         this.fireEvent("autosize", this, w);
21430     },
21431     
21432     // private
21433     SafariOnKeyDown : function(event)
21434     {
21435         // this is a workaround for a password hang bug on chrome/ webkit.
21436         
21437         var isSelectAll = false;
21438         
21439         if(this.el.dom.selectionEnd > 0){
21440             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21441         }
21442         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21443             event.preventDefault();
21444             this.setValue('');
21445             return;
21446         }
21447         
21448         if(isSelectAll){ // backspace and delete key
21449             
21450             event.preventDefault();
21451             // this is very hacky as keydown always get's upper case.
21452             //
21453             var cc = String.fromCharCode(event.getCharCode());
21454             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21455             
21456         }
21457         
21458         
21459     }
21460 });/*
21461  * Based on:
21462  * Ext JS Library 1.1.1
21463  * Copyright(c) 2006-2007, Ext JS, LLC.
21464  *
21465  * Originally Released Under LGPL - original licence link has changed is not relivant.
21466  *
21467  * Fork - LGPL
21468  * <script type="text/javascript">
21469  */
21470  
21471 /**
21472  * @class Roo.form.Hidden
21473  * @extends Roo.form.TextField
21474  * Simple Hidden element used on forms 
21475  * 
21476  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21477  * 
21478  * @constructor
21479  * Creates a new Hidden form element.
21480  * @param {Object} config Configuration options
21481  */
21482
21483
21484
21485 // easy hidden field...
21486 Roo.form.Hidden = function(config){
21487     Roo.form.Hidden.superclass.constructor.call(this, config);
21488 };
21489   
21490 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21491     fieldLabel:      '',
21492     inputType:      'hidden',
21493     width:          50,
21494     allowBlank:     true,
21495     labelSeparator: '',
21496     hidden:         true,
21497     itemCls :       'x-form-item-display-none'
21498
21499
21500 });
21501
21502
21503 /*
21504  * Based on:
21505  * Ext JS Library 1.1.1
21506  * Copyright(c) 2006-2007, Ext JS, LLC.
21507  *
21508  * Originally Released Under LGPL - original licence link has changed is not relivant.
21509  *
21510  * Fork - LGPL
21511  * <script type="text/javascript">
21512  */
21513  
21514 /**
21515  * @class Roo.form.TriggerField
21516  * @extends Roo.form.TextField
21517  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21518  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21519  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21520  * for which you can provide a custom implementation.  For example:
21521  * <pre><code>
21522 var trigger = new Roo.form.TriggerField();
21523 trigger.onTriggerClick = myTriggerFn;
21524 trigger.applyTo('my-field');
21525 </code></pre>
21526  *
21527  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21528  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21529  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21530  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21531  * @constructor
21532  * Create a new TriggerField.
21533  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21534  * to the base TextField)
21535  */
21536 Roo.form.TriggerField = function(config){
21537     this.mimicing = false;
21538     Roo.form.TriggerField.superclass.constructor.call(this, config);
21539 };
21540
21541 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21542     /**
21543      * @cfg {String} triggerClass A CSS class to apply to the trigger
21544      */
21545     /**
21546      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21547      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21548      */
21549     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21550     /**
21551      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21552      */
21553     hideTrigger:false,
21554
21555     /** @cfg {Boolean} grow @hide */
21556     /** @cfg {Number} growMin @hide */
21557     /** @cfg {Number} growMax @hide */
21558
21559     /**
21560      * @hide 
21561      * @method
21562      */
21563     autoSize: Roo.emptyFn,
21564     // private
21565     monitorTab : true,
21566     // private
21567     deferHeight : true,
21568
21569     
21570     actionMode : 'wrap',
21571     // private
21572     onResize : function(w, h){
21573         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21574         if(typeof w == 'number'){
21575             var x = w - this.trigger.getWidth();
21576             this.el.setWidth(this.adjustWidth('input', x));
21577             this.trigger.setStyle('left', x+'px');
21578         }
21579     },
21580
21581     // private
21582     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21583
21584     // private
21585     getResizeEl : function(){
21586         return this.wrap;
21587     },
21588
21589     // private
21590     getPositionEl : function(){
21591         return this.wrap;
21592     },
21593
21594     // private
21595     alignErrorIcon : function(){
21596         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21597     },
21598
21599     // private
21600     onRender : function(ct, position){
21601         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21602         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21603         this.trigger = this.wrap.createChild(this.triggerConfig ||
21604                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21605         if(this.hideTrigger){
21606             this.trigger.setDisplayed(false);
21607         }
21608         this.initTrigger();
21609         if(!this.width){
21610             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21611         }
21612     },
21613
21614     // private
21615     initTrigger : function(){
21616         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21617         this.trigger.addClassOnOver('x-form-trigger-over');
21618         this.trigger.addClassOnClick('x-form-trigger-click');
21619     },
21620
21621     // private
21622     onDestroy : function(){
21623         if(this.trigger){
21624             this.trigger.removeAllListeners();
21625             this.trigger.remove();
21626         }
21627         if(this.wrap){
21628             this.wrap.remove();
21629         }
21630         Roo.form.TriggerField.superclass.onDestroy.call(this);
21631     },
21632
21633     // private
21634     onFocus : function(){
21635         Roo.form.TriggerField.superclass.onFocus.call(this);
21636         if(!this.mimicing){
21637             this.wrap.addClass('x-trigger-wrap-focus');
21638             this.mimicing = true;
21639             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21640             if(this.monitorTab){
21641                 this.el.on("keydown", this.checkTab, this);
21642             }
21643         }
21644     },
21645
21646     // private
21647     checkTab : function(e){
21648         if(e.getKey() == e.TAB){
21649             this.triggerBlur();
21650         }
21651     },
21652
21653     // private
21654     onBlur : function(){
21655         // do nothing
21656     },
21657
21658     // private
21659     mimicBlur : function(e, t){
21660         if(!this.wrap.contains(t) && this.validateBlur()){
21661             this.triggerBlur();
21662         }
21663     },
21664
21665     // private
21666     triggerBlur : function(){
21667         this.mimicing = false;
21668         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21669         if(this.monitorTab){
21670             this.el.un("keydown", this.checkTab, this);
21671         }
21672         this.wrap.removeClass('x-trigger-wrap-focus');
21673         Roo.form.TriggerField.superclass.onBlur.call(this);
21674     },
21675
21676     // private
21677     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21678     validateBlur : function(e, t){
21679         return true;
21680     },
21681
21682     // private
21683     onDisable : function(){
21684         Roo.form.TriggerField.superclass.onDisable.call(this);
21685         if(this.wrap){
21686             this.wrap.addClass('x-item-disabled');
21687         }
21688     },
21689
21690     // private
21691     onEnable : function(){
21692         Roo.form.TriggerField.superclass.onEnable.call(this);
21693         if(this.wrap){
21694             this.wrap.removeClass('x-item-disabled');
21695         }
21696     },
21697
21698     // private
21699     onShow : function(){
21700         var ae = this.getActionEl();
21701         
21702         if(ae){
21703             ae.dom.style.display = '';
21704             ae.dom.style.visibility = 'visible';
21705         }
21706     },
21707
21708     // private
21709     
21710     onHide : function(){
21711         var ae = this.getActionEl();
21712         ae.dom.style.display = 'none';
21713     },
21714
21715     /**
21716      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21717      * by an implementing function.
21718      * @method
21719      * @param {EventObject} e
21720      */
21721     onTriggerClick : Roo.emptyFn
21722 });
21723
21724 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21725 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21726 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21727 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21728     initComponent : function(){
21729         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21730
21731         this.triggerConfig = {
21732             tag:'span', cls:'x-form-twin-triggers', cn:[
21733             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21734             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21735         ]};
21736     },
21737
21738     getTrigger : function(index){
21739         return this.triggers[index];
21740     },
21741
21742     initTrigger : function(){
21743         var ts = this.trigger.select('.x-form-trigger', true);
21744         this.wrap.setStyle('overflow', 'hidden');
21745         var triggerField = this;
21746         ts.each(function(t, all, index){
21747             t.hide = function(){
21748                 var w = triggerField.wrap.getWidth();
21749                 this.dom.style.display = 'none';
21750                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21751             };
21752             t.show = function(){
21753                 var w = triggerField.wrap.getWidth();
21754                 this.dom.style.display = '';
21755                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21756             };
21757             var triggerIndex = 'Trigger'+(index+1);
21758
21759             if(this['hide'+triggerIndex]){
21760                 t.dom.style.display = 'none';
21761             }
21762             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21763             t.addClassOnOver('x-form-trigger-over');
21764             t.addClassOnClick('x-form-trigger-click');
21765         }, this);
21766         this.triggers = ts.elements;
21767     },
21768
21769     onTrigger1Click : Roo.emptyFn,
21770     onTrigger2Click : Roo.emptyFn
21771 });/*
21772  * Based on:
21773  * Ext JS Library 1.1.1
21774  * Copyright(c) 2006-2007, Ext JS, LLC.
21775  *
21776  * Originally Released Under LGPL - original licence link has changed is not relivant.
21777  *
21778  * Fork - LGPL
21779  * <script type="text/javascript">
21780  */
21781  
21782 /**
21783  * @class Roo.form.TextArea
21784  * @extends Roo.form.TextField
21785  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21786  * support for auto-sizing.
21787  * @constructor
21788  * Creates a new TextArea
21789  * @param {Object} config Configuration options
21790  */
21791 Roo.form.TextArea = function(config){
21792     Roo.form.TextArea.superclass.constructor.call(this, config);
21793     // these are provided exchanges for backwards compat
21794     // minHeight/maxHeight were replaced by growMin/growMax to be
21795     // compatible with TextField growing config values
21796     if(this.minHeight !== undefined){
21797         this.growMin = this.minHeight;
21798     }
21799     if(this.maxHeight !== undefined){
21800         this.growMax = this.maxHeight;
21801     }
21802 };
21803
21804 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21805     /**
21806      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21807      */
21808     growMin : 60,
21809     /**
21810      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21811      */
21812     growMax: 1000,
21813     /**
21814      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21815      * in the field (equivalent to setting overflow: hidden, defaults to false)
21816      */
21817     preventScrollbars: false,
21818     /**
21819      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21820      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21821      */
21822
21823     // private
21824     onRender : function(ct, position){
21825         if(!this.el){
21826             this.defaultAutoCreate = {
21827                 tag: "textarea",
21828                 style:"width:300px;height:60px;",
21829                 autocomplete: "off"
21830             };
21831         }
21832         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21833         if(this.grow){
21834             this.textSizeEl = Roo.DomHelper.append(document.body, {
21835                 tag: "pre", cls: "x-form-grow-sizer"
21836             });
21837             if(this.preventScrollbars){
21838                 this.el.setStyle("overflow", "hidden");
21839             }
21840             this.el.setHeight(this.growMin);
21841         }
21842     },
21843
21844     onDestroy : function(){
21845         if(this.textSizeEl){
21846             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21847         }
21848         Roo.form.TextArea.superclass.onDestroy.call(this);
21849     },
21850
21851     // private
21852     onKeyUp : function(e){
21853         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21854             this.autoSize();
21855         }
21856     },
21857
21858     /**
21859      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21860      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21861      */
21862     autoSize : function(){
21863         if(!this.grow || !this.textSizeEl){
21864             return;
21865         }
21866         var el = this.el;
21867         var v = el.dom.value;
21868         var ts = this.textSizeEl;
21869
21870         ts.innerHTML = '';
21871         ts.appendChild(document.createTextNode(v));
21872         v = ts.innerHTML;
21873
21874         Roo.fly(ts).setWidth(this.el.getWidth());
21875         if(v.length < 1){
21876             v = "&#160;&#160;";
21877         }else{
21878             if(Roo.isIE){
21879                 v = v.replace(/\n/g, '<p>&#160;</p>');
21880             }
21881             v += "&#160;\n&#160;";
21882         }
21883         ts.innerHTML = v;
21884         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21885         if(h != this.lastHeight){
21886             this.lastHeight = h;
21887             this.el.setHeight(h);
21888             this.fireEvent("autosize", this, h);
21889         }
21890     }
21891 });/*
21892  * Based on:
21893  * Ext JS Library 1.1.1
21894  * Copyright(c) 2006-2007, Ext JS, LLC.
21895  *
21896  * Originally Released Under LGPL - original licence link has changed is not relivant.
21897  *
21898  * Fork - LGPL
21899  * <script type="text/javascript">
21900  */
21901  
21902
21903 /**
21904  * @class Roo.form.NumberField
21905  * @extends Roo.form.TextField
21906  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21907  * @constructor
21908  * Creates a new NumberField
21909  * @param {Object} config Configuration options
21910  */
21911 Roo.form.NumberField = function(config){
21912     Roo.form.NumberField.superclass.constructor.call(this, config);
21913 };
21914
21915 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21916     /**
21917      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21918      */
21919     fieldClass: "x-form-field x-form-num-field",
21920     /**
21921      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21922      */
21923     allowDecimals : true,
21924     /**
21925      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21926      */
21927     decimalSeparator : ".",
21928     /**
21929      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21930      */
21931     decimalPrecision : 2,
21932     /**
21933      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21934      */
21935     allowNegative : true,
21936     /**
21937      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21938      */
21939     minValue : Number.NEGATIVE_INFINITY,
21940     /**
21941      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21942      */
21943     maxValue : Number.MAX_VALUE,
21944     /**
21945      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21946      */
21947     minText : "The minimum value for this field is {0}",
21948     /**
21949      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21950      */
21951     maxText : "The maximum value for this field is {0}",
21952     /**
21953      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21954      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21955      */
21956     nanText : "{0} is not a valid number",
21957
21958     // private
21959     initEvents : function(){
21960         Roo.form.NumberField.superclass.initEvents.call(this);
21961         var allowed = "0123456789";
21962         if(this.allowDecimals){
21963             allowed += this.decimalSeparator;
21964         }
21965         if(this.allowNegative){
21966             allowed += "-";
21967         }
21968         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21969         var keyPress = function(e){
21970             var k = e.getKey();
21971             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21972                 return;
21973             }
21974             var c = e.getCharCode();
21975             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21976                 e.stopEvent();
21977             }
21978         };
21979         this.el.on("keypress", keyPress, this);
21980     },
21981
21982     // private
21983     validateValue : function(value){
21984         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21985             return false;
21986         }
21987         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21988              return true;
21989         }
21990         var num = this.parseValue(value);
21991         if(isNaN(num)){
21992             this.markInvalid(String.format(this.nanText, value));
21993             return false;
21994         }
21995         if(num < this.minValue){
21996             this.markInvalid(String.format(this.minText, this.minValue));
21997             return false;
21998         }
21999         if(num > this.maxValue){
22000             this.markInvalid(String.format(this.maxText, this.maxValue));
22001             return false;
22002         }
22003         return true;
22004     },
22005
22006     getValue : function(){
22007         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22008     },
22009
22010     // private
22011     parseValue : function(value){
22012         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22013         return isNaN(value) ? '' : value;
22014     },
22015
22016     // private
22017     fixPrecision : function(value){
22018         var nan = isNaN(value);
22019         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22020             return nan ? '' : value;
22021         }
22022         return parseFloat(value).toFixed(this.decimalPrecision);
22023     },
22024
22025     setValue : function(v){
22026         v = this.fixPrecision(v);
22027         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22028     },
22029
22030     // private
22031     decimalPrecisionFcn : function(v){
22032         return Math.floor(v);
22033     },
22034
22035     beforeBlur : function(){
22036         var v = this.parseValue(this.getRawValue());
22037         if(v){
22038             this.setValue(v);
22039         }
22040     }
22041 });/*
22042  * Based on:
22043  * Ext JS Library 1.1.1
22044  * Copyright(c) 2006-2007, Ext JS, LLC.
22045  *
22046  * Originally Released Under LGPL - original licence link has changed is not relivant.
22047  *
22048  * Fork - LGPL
22049  * <script type="text/javascript">
22050  */
22051  
22052 /**
22053  * @class Roo.form.DateField
22054  * @extends Roo.form.TriggerField
22055  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22056 * @constructor
22057 * Create a new DateField
22058 * @param {Object} config
22059  */
22060 Roo.form.DateField = function(config){
22061     Roo.form.DateField.superclass.constructor.call(this, config);
22062     
22063       this.addEvents({
22064          
22065         /**
22066          * @event select
22067          * Fires when a date is selected
22068              * @param {Roo.form.DateField} combo This combo box
22069              * @param {Date} date The date selected
22070              */
22071         'select' : true
22072          
22073     });
22074     
22075     
22076     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22077     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22078     this.ddMatch = null;
22079     if(this.disabledDates){
22080         var dd = this.disabledDates;
22081         var re = "(?:";
22082         for(var i = 0; i < dd.length; i++){
22083             re += dd[i];
22084             if(i != dd.length-1) re += "|";
22085         }
22086         this.ddMatch = new RegExp(re + ")");
22087     }
22088 };
22089
22090 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22091     /**
22092      * @cfg {String} format
22093      * The default date format string which can be overriden for localization support.  The format must be
22094      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22095      */
22096     format : "m/d/y",
22097     /**
22098      * @cfg {String} altFormats
22099      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22100      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22101      */
22102     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22103     /**
22104      * @cfg {Array} disabledDays
22105      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22106      */
22107     disabledDays : null,
22108     /**
22109      * @cfg {String} disabledDaysText
22110      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22111      */
22112     disabledDaysText : "Disabled",
22113     /**
22114      * @cfg {Array} disabledDates
22115      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22116      * expression so they are very powerful. Some examples:
22117      * <ul>
22118      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22119      * <li>["03/08", "09/16"] would disable those days for every year</li>
22120      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22121      * <li>["03/../2006"] would disable every day in March 2006</li>
22122      * <li>["^03"] would disable every day in every March</li>
22123      * </ul>
22124      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22125      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22126      */
22127     disabledDates : null,
22128     /**
22129      * @cfg {String} disabledDatesText
22130      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22131      */
22132     disabledDatesText : "Disabled",
22133     /**
22134      * @cfg {Date/String} minValue
22135      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22136      * valid format (defaults to null).
22137      */
22138     minValue : null,
22139     /**
22140      * @cfg {Date/String} maxValue
22141      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22142      * valid format (defaults to null).
22143      */
22144     maxValue : null,
22145     /**
22146      * @cfg {String} minText
22147      * The error text to display when the date in the cell is before minValue (defaults to
22148      * 'The date in this field must be after {minValue}').
22149      */
22150     minText : "The date in this field must be equal to or after {0}",
22151     /**
22152      * @cfg {String} maxText
22153      * The error text to display when the date in the cell is after maxValue (defaults to
22154      * 'The date in this field must be before {maxValue}').
22155      */
22156     maxText : "The date in this field must be equal to or before {0}",
22157     /**
22158      * @cfg {String} invalidText
22159      * The error text to display when the date in the field is invalid (defaults to
22160      * '{value} is not a valid date - it must be in the format {format}').
22161      */
22162     invalidText : "{0} is not a valid date - it must be in the format {1}",
22163     /**
22164      * @cfg {String} triggerClass
22165      * An additional CSS class used to style the trigger button.  The trigger will always get the
22166      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22167      * which displays a calendar icon).
22168      */
22169     triggerClass : 'x-form-date-trigger',
22170     
22171
22172     /**
22173      * @cfg {Boolean} useIso
22174      * if enabled, then the date field will use a hidden field to store the 
22175      * real value as iso formated date. default (false)
22176      */ 
22177     useIso : false,
22178     /**
22179      * @cfg {String/Object} autoCreate
22180      * A DomHelper element spec, or true for a default element spec (defaults to
22181      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22182      */ 
22183     // private
22184     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22185     
22186     // private
22187     hiddenField: false,
22188     
22189     onRender : function(ct, position)
22190     {
22191         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22192         if (this.useIso) {
22193             //this.el.dom.removeAttribute('name'); 
22194             Roo.log("Changing name?");
22195             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22196             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22197                     'before', true);
22198             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22199             // prevent input submission
22200             this.hiddenName = this.name;
22201         }
22202             
22203             
22204     },
22205     
22206     // private
22207     validateValue : function(value)
22208     {
22209         value = this.formatDate(value);
22210         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22211             Roo.log('super failed');
22212             return false;
22213         }
22214         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22215              return true;
22216         }
22217         var svalue = value;
22218         value = this.parseDate(value);
22219         if(!value){
22220             Roo.log('parse date failed' + svalue);
22221             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22222             return false;
22223         }
22224         var time = value.getTime();
22225         if(this.minValue && time < this.minValue.getTime()){
22226             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22227             return false;
22228         }
22229         if(this.maxValue && time > this.maxValue.getTime()){
22230             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22231             return false;
22232         }
22233         if(this.disabledDays){
22234             var day = value.getDay();
22235             for(var i = 0; i < this.disabledDays.length; i++) {
22236                 if(day === this.disabledDays[i]){
22237                     this.markInvalid(this.disabledDaysText);
22238                     return false;
22239                 }
22240             }
22241         }
22242         var fvalue = this.formatDate(value);
22243         if(this.ddMatch && this.ddMatch.test(fvalue)){
22244             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22245             return false;
22246         }
22247         return true;
22248     },
22249
22250     // private
22251     // Provides logic to override the default TriggerField.validateBlur which just returns true
22252     validateBlur : function(){
22253         return !this.menu || !this.menu.isVisible();
22254     },
22255     
22256     getName: function()
22257     {
22258         // returns hidden if it's set..
22259         if (!this.rendered) {return ''};
22260         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22261         
22262     },
22263
22264     /**
22265      * Returns the current date value of the date field.
22266      * @return {Date} The date value
22267      */
22268     getValue : function(){
22269         
22270         return  this.hiddenField ?
22271                 this.hiddenField.value :
22272                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22273     },
22274
22275     /**
22276      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22277      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22278      * (the default format used is "m/d/y").
22279      * <br />Usage:
22280      * <pre><code>
22281 //All of these calls set the same date value (May 4, 2006)
22282
22283 //Pass a date object:
22284 var dt = new Date('5/4/06');
22285 dateField.setValue(dt);
22286
22287 //Pass a date string (default format):
22288 dateField.setValue('5/4/06');
22289
22290 //Pass a date string (custom format):
22291 dateField.format = 'Y-m-d';
22292 dateField.setValue('2006-5-4');
22293 </code></pre>
22294      * @param {String/Date} date The date or valid date string
22295      */
22296     setValue : function(date){
22297         if (this.hiddenField) {
22298             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22299         }
22300         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22301         // make sure the value field is always stored as a date..
22302         this.value = this.parseDate(date);
22303         
22304         
22305     },
22306
22307     // private
22308     parseDate : function(value){
22309         if(!value || value instanceof Date){
22310             return value;
22311         }
22312         var v = Date.parseDate(value, this.format);
22313          if (!v && this.useIso) {
22314             v = Date.parseDate(value, 'Y-m-d');
22315         }
22316         if(!v && this.altFormats){
22317             if(!this.altFormatsArray){
22318                 this.altFormatsArray = this.altFormats.split("|");
22319             }
22320             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22321                 v = Date.parseDate(value, this.altFormatsArray[i]);
22322             }
22323         }
22324         return v;
22325     },
22326
22327     // private
22328     formatDate : function(date, fmt){
22329         return (!date || !(date instanceof Date)) ?
22330                date : date.dateFormat(fmt || this.format);
22331     },
22332
22333     // private
22334     menuListeners : {
22335         select: function(m, d){
22336             
22337             this.setValue(d);
22338             this.fireEvent('select', this, d);
22339         },
22340         show : function(){ // retain focus styling
22341             this.onFocus();
22342         },
22343         hide : function(){
22344             this.focus.defer(10, this);
22345             var ml = this.menuListeners;
22346             this.menu.un("select", ml.select,  this);
22347             this.menu.un("show", ml.show,  this);
22348             this.menu.un("hide", ml.hide,  this);
22349         }
22350     },
22351
22352     // private
22353     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22354     onTriggerClick : function(){
22355         if(this.disabled){
22356             return;
22357         }
22358         if(this.menu == null){
22359             this.menu = new Roo.menu.DateMenu();
22360         }
22361         Roo.apply(this.menu.picker,  {
22362             showClear: this.allowBlank,
22363             minDate : this.minValue,
22364             maxDate : this.maxValue,
22365             disabledDatesRE : this.ddMatch,
22366             disabledDatesText : this.disabledDatesText,
22367             disabledDays : this.disabledDays,
22368             disabledDaysText : this.disabledDaysText,
22369             format : this.useIso ? 'Y-m-d' : this.format,
22370             minText : String.format(this.minText, this.formatDate(this.minValue)),
22371             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22372         });
22373         this.menu.on(Roo.apply({}, this.menuListeners, {
22374             scope:this
22375         }));
22376         this.menu.picker.setValue(this.getValue() || new Date());
22377         this.menu.show(this.el, "tl-bl?");
22378     },
22379
22380     beforeBlur : function(){
22381         var v = this.parseDate(this.getRawValue());
22382         if(v){
22383             this.setValue(v);
22384         }
22385     },
22386
22387     /*@
22388      * overide
22389      * 
22390      */
22391     isDirty : function() {
22392         if(this.disabled) {
22393             return false;
22394         }
22395         
22396         if(typeof(this.startValue) === 'undefined'){
22397             return false;
22398         }
22399         
22400         return String(this.getValue()) !== String(this.startValue);
22401         
22402     }
22403 });/*
22404  * Based on:
22405  * Ext JS Library 1.1.1
22406  * Copyright(c) 2006-2007, Ext JS, LLC.
22407  *
22408  * Originally Released Under LGPL - original licence link has changed is not relivant.
22409  *
22410  * Fork - LGPL
22411  * <script type="text/javascript">
22412  */
22413  
22414 /**
22415  * @class Roo.form.MonthField
22416  * @extends Roo.form.TriggerField
22417  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22418 * @constructor
22419 * Create a new MonthField
22420 * @param {Object} config
22421  */
22422 Roo.form.MonthField = function(config){
22423     
22424     Roo.form.MonthField.superclass.constructor.call(this, config);
22425     
22426       this.addEvents({
22427          
22428         /**
22429          * @event select
22430          * Fires when a date is selected
22431              * @param {Roo.form.MonthFieeld} combo This combo box
22432              * @param {Date} date The date selected
22433              */
22434         'select' : true
22435          
22436     });
22437     
22438     
22439     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22440     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22441     this.ddMatch = null;
22442     if(this.disabledDates){
22443         var dd = this.disabledDates;
22444         var re = "(?:";
22445         for(var i = 0; i < dd.length; i++){
22446             re += dd[i];
22447             if(i != dd.length-1) re += "|";
22448         }
22449         this.ddMatch = new RegExp(re + ")");
22450     }
22451 };
22452
22453 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22454     /**
22455      * @cfg {String} format
22456      * The default date format string which can be overriden for localization support.  The format must be
22457      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22458      */
22459     format : "M Y",
22460     /**
22461      * @cfg {String} altFormats
22462      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22463      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22464      */
22465     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22466     /**
22467      * @cfg {Array} disabledDays
22468      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22469      */
22470     disabledDays : [0,1,2,3,4,5,6],
22471     /**
22472      * @cfg {String} disabledDaysText
22473      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22474      */
22475     disabledDaysText : "Disabled",
22476     /**
22477      * @cfg {Array} disabledDates
22478      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22479      * expression so they are very powerful. Some examples:
22480      * <ul>
22481      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22482      * <li>["03/08", "09/16"] would disable those days for every year</li>
22483      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22484      * <li>["03/../2006"] would disable every day in March 2006</li>
22485      * <li>["^03"] would disable every day in every March</li>
22486      * </ul>
22487      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22488      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22489      */
22490     disabledDates : null,
22491     /**
22492      * @cfg {String} disabledDatesText
22493      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22494      */
22495     disabledDatesText : "Disabled",
22496     /**
22497      * @cfg {Date/String} minValue
22498      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22499      * valid format (defaults to null).
22500      */
22501     minValue : null,
22502     /**
22503      * @cfg {Date/String} maxValue
22504      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22505      * valid format (defaults to null).
22506      */
22507     maxValue : null,
22508     /**
22509      * @cfg {String} minText
22510      * The error text to display when the date in the cell is before minValue (defaults to
22511      * 'The date in this field must be after {minValue}').
22512      */
22513     minText : "The date in this field must be equal to or after {0}",
22514     /**
22515      * @cfg {String} maxTextf
22516      * The error text to display when the date in the cell is after maxValue (defaults to
22517      * 'The date in this field must be before {maxValue}').
22518      */
22519     maxText : "The date in this field must be equal to or before {0}",
22520     /**
22521      * @cfg {String} invalidText
22522      * The error text to display when the date in the field is invalid (defaults to
22523      * '{value} is not a valid date - it must be in the format {format}').
22524      */
22525     invalidText : "{0} is not a valid date - it must be in the format {1}",
22526     /**
22527      * @cfg {String} triggerClass
22528      * An additional CSS class used to style the trigger button.  The trigger will always get the
22529      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22530      * which displays a calendar icon).
22531      */
22532     triggerClass : 'x-form-date-trigger',
22533     
22534
22535     /**
22536      * @cfg {Boolean} useIso
22537      * if enabled, then the date field will use a hidden field to store the 
22538      * real value as iso formated date. default (true)
22539      */ 
22540     useIso : true,
22541     /**
22542      * @cfg {String/Object} autoCreate
22543      * A DomHelper element spec, or true for a default element spec (defaults to
22544      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22545      */ 
22546     // private
22547     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22548     
22549     // private
22550     hiddenField: false,
22551     
22552     hideMonthPicker : false,
22553     
22554     onRender : function(ct, position)
22555     {
22556         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22557         if (this.useIso) {
22558             this.el.dom.removeAttribute('name'); 
22559             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22560                     'before', true);
22561             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22562             // prevent input submission
22563             this.hiddenName = this.name;
22564         }
22565             
22566             
22567     },
22568     
22569     // private
22570     validateValue : function(value)
22571     {
22572         value = this.formatDate(value);
22573         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22574             return false;
22575         }
22576         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22577              return true;
22578         }
22579         var svalue = value;
22580         value = this.parseDate(value);
22581         if(!value){
22582             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22583             return false;
22584         }
22585         var time = value.getTime();
22586         if(this.minValue && time < this.minValue.getTime()){
22587             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22588             return false;
22589         }
22590         if(this.maxValue && time > this.maxValue.getTime()){
22591             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22592             return false;
22593         }
22594         /*if(this.disabledDays){
22595             var day = value.getDay();
22596             for(var i = 0; i < this.disabledDays.length; i++) {
22597                 if(day === this.disabledDays[i]){
22598                     this.markInvalid(this.disabledDaysText);
22599                     return false;
22600                 }
22601             }
22602         }
22603         */
22604         var fvalue = this.formatDate(value);
22605         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22606             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22607             return false;
22608         }
22609         */
22610         return true;
22611     },
22612
22613     // private
22614     // Provides logic to override the default TriggerField.validateBlur which just returns true
22615     validateBlur : function(){
22616         return !this.menu || !this.menu.isVisible();
22617     },
22618
22619     /**
22620      * Returns the current date value of the date field.
22621      * @return {Date} The date value
22622      */
22623     getValue : function(){
22624         
22625         
22626         
22627         return  this.hiddenField ?
22628                 this.hiddenField.value :
22629                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22630     },
22631
22632     /**
22633      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22634      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22635      * (the default format used is "m/d/y").
22636      * <br />Usage:
22637      * <pre><code>
22638 //All of these calls set the same date value (May 4, 2006)
22639
22640 //Pass a date object:
22641 var dt = new Date('5/4/06');
22642 monthField.setValue(dt);
22643
22644 //Pass a date string (default format):
22645 monthField.setValue('5/4/06');
22646
22647 //Pass a date string (custom format):
22648 monthField.format = 'Y-m-d';
22649 monthField.setValue('2006-5-4');
22650 </code></pre>
22651      * @param {String/Date} date The date or valid date string
22652      */
22653     setValue : function(date){
22654         Roo.log('month setValue' + date);
22655         // can only be first of month..
22656         
22657         var val = this.parseDate(date);
22658         
22659         if (this.hiddenField) {
22660             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22661         }
22662         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22663         this.value = this.parseDate(date);
22664     },
22665
22666     // private
22667     parseDate : function(value){
22668         if(!value || value instanceof Date){
22669             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22670             return value;
22671         }
22672         var v = Date.parseDate(value, this.format);
22673         if (!v && this.useIso) {
22674             v = Date.parseDate(value, 'Y-m-d');
22675         }
22676         if (v) {
22677             // 
22678             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22679         }
22680         
22681         
22682         if(!v && this.altFormats){
22683             if(!this.altFormatsArray){
22684                 this.altFormatsArray = this.altFormats.split("|");
22685             }
22686             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22687                 v = Date.parseDate(value, this.altFormatsArray[i]);
22688             }
22689         }
22690         return v;
22691     },
22692
22693     // private
22694     formatDate : function(date, fmt){
22695         return (!date || !(date instanceof Date)) ?
22696                date : date.dateFormat(fmt || this.format);
22697     },
22698
22699     // private
22700     menuListeners : {
22701         select: function(m, d){
22702             this.setValue(d);
22703             this.fireEvent('select', this, d);
22704         },
22705         show : function(){ // retain focus styling
22706             this.onFocus();
22707         },
22708         hide : function(){
22709             this.focus.defer(10, this);
22710             var ml = this.menuListeners;
22711             this.menu.un("select", ml.select,  this);
22712             this.menu.un("show", ml.show,  this);
22713             this.menu.un("hide", ml.hide,  this);
22714         }
22715     },
22716     // private
22717     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22718     onTriggerClick : function(){
22719         if(this.disabled){
22720             return;
22721         }
22722         if(this.menu == null){
22723             this.menu = new Roo.menu.DateMenu();
22724            
22725         }
22726         
22727         Roo.apply(this.menu.picker,  {
22728             
22729             showClear: this.allowBlank,
22730             minDate : this.minValue,
22731             maxDate : this.maxValue,
22732             disabledDatesRE : this.ddMatch,
22733             disabledDatesText : this.disabledDatesText,
22734             
22735             format : this.useIso ? 'Y-m-d' : this.format,
22736             minText : String.format(this.minText, this.formatDate(this.minValue)),
22737             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22738             
22739         });
22740          this.menu.on(Roo.apply({}, this.menuListeners, {
22741             scope:this
22742         }));
22743        
22744         
22745         var m = this.menu;
22746         var p = m.picker;
22747         
22748         // hide month picker get's called when we called by 'before hide';
22749         
22750         var ignorehide = true;
22751         p.hideMonthPicker  = function(disableAnim){
22752             if (ignorehide) {
22753                 return;
22754             }
22755              if(this.monthPicker){
22756                 Roo.log("hideMonthPicker called");
22757                 if(disableAnim === true){
22758                     this.monthPicker.hide();
22759                 }else{
22760                     this.monthPicker.slideOut('t', {duration:.2});
22761                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22762                     p.fireEvent("select", this, this.value);
22763                     m.hide();
22764                 }
22765             }
22766         }
22767         
22768         Roo.log('picker set value');
22769         Roo.log(this.getValue());
22770         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22771         m.show(this.el, 'tl-bl?');
22772         ignorehide  = false;
22773         // this will trigger hideMonthPicker..
22774         
22775         
22776         // hidden the day picker
22777         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22778         
22779         
22780         
22781       
22782         
22783         p.showMonthPicker.defer(100, p);
22784     
22785         
22786        
22787     },
22788
22789     beforeBlur : function(){
22790         var v = this.parseDate(this.getRawValue());
22791         if(v){
22792             this.setValue(v);
22793         }
22794     }
22795
22796     /** @cfg {Boolean} grow @hide */
22797     /** @cfg {Number} growMin @hide */
22798     /** @cfg {Number} growMax @hide */
22799     /**
22800      * @hide
22801      * @method autoSize
22802      */
22803 });/*
22804  * Based on:
22805  * Ext JS Library 1.1.1
22806  * Copyright(c) 2006-2007, Ext JS, LLC.
22807  *
22808  * Originally Released Under LGPL - original licence link has changed is not relivant.
22809  *
22810  * Fork - LGPL
22811  * <script type="text/javascript">
22812  */
22813  
22814
22815 /**
22816  * @class Roo.form.ComboBox
22817  * @extends Roo.form.TriggerField
22818  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22819  * @constructor
22820  * Create a new ComboBox.
22821  * @param {Object} config Configuration options
22822  */
22823 Roo.form.ComboBox = function(config){
22824     Roo.form.ComboBox.superclass.constructor.call(this, config);
22825     this.addEvents({
22826         /**
22827          * @event expand
22828          * Fires when the dropdown list is expanded
22829              * @param {Roo.form.ComboBox} combo This combo box
22830              */
22831         'expand' : true,
22832         /**
22833          * @event collapse
22834          * Fires when the dropdown list is collapsed
22835              * @param {Roo.form.ComboBox} combo This combo box
22836              */
22837         'collapse' : true,
22838         /**
22839          * @event beforeselect
22840          * Fires before a list item is selected. Return false to cancel the selection.
22841              * @param {Roo.form.ComboBox} combo This combo box
22842              * @param {Roo.data.Record} record The data record returned from the underlying store
22843              * @param {Number} index The index of the selected item in the dropdown list
22844              */
22845         'beforeselect' : true,
22846         /**
22847          * @event select
22848          * Fires when a list item is selected
22849              * @param {Roo.form.ComboBox} combo This combo box
22850              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22851              * @param {Number} index The index of the selected item in the dropdown list
22852              */
22853         'select' : true,
22854         /**
22855          * @event beforequery
22856          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22857          * The event object passed has these properties:
22858              * @param {Roo.form.ComboBox} combo This combo box
22859              * @param {String} query The query
22860              * @param {Boolean} forceAll true to force "all" query
22861              * @param {Boolean} cancel true to cancel the query
22862              * @param {Object} e The query event object
22863              */
22864         'beforequery': true,
22865          /**
22866          * @event add
22867          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22868              * @param {Roo.form.ComboBox} combo This combo box
22869              */
22870         'add' : true,
22871         /**
22872          * @event edit
22873          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22874              * @param {Roo.form.ComboBox} combo This combo box
22875              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22876              */
22877         'edit' : true
22878         
22879         
22880     });
22881     if(this.transform){
22882         this.allowDomMove = false;
22883         var s = Roo.getDom(this.transform);
22884         if(!this.hiddenName){
22885             this.hiddenName = s.name;
22886         }
22887         if(!this.store){
22888             this.mode = 'local';
22889             var d = [], opts = s.options;
22890             for(var i = 0, len = opts.length;i < len; i++){
22891                 var o = opts[i];
22892                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22893                 if(o.selected) {
22894                     this.value = value;
22895                 }
22896                 d.push([value, o.text]);
22897             }
22898             this.store = new Roo.data.SimpleStore({
22899                 'id': 0,
22900                 fields: ['value', 'text'],
22901                 data : d
22902             });
22903             this.valueField = 'value';
22904             this.displayField = 'text';
22905         }
22906         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22907         if(!this.lazyRender){
22908             this.target = true;
22909             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22910             s.parentNode.removeChild(s); // remove it
22911             this.render(this.el.parentNode);
22912         }else{
22913             s.parentNode.removeChild(s); // remove it
22914         }
22915
22916     }
22917     if (this.store) {
22918         this.store = Roo.factory(this.store, Roo.data);
22919     }
22920     
22921     this.selectedIndex = -1;
22922     if(this.mode == 'local'){
22923         if(config.queryDelay === undefined){
22924             this.queryDelay = 10;
22925         }
22926         if(config.minChars === undefined){
22927             this.minChars = 0;
22928         }
22929     }
22930 };
22931
22932 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22933     /**
22934      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22935      */
22936     /**
22937      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22938      * rendering into an Roo.Editor, defaults to false)
22939      */
22940     /**
22941      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22942      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22943      */
22944     /**
22945      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22946      */
22947     /**
22948      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22949      * the dropdown list (defaults to undefined, with no header element)
22950      */
22951
22952      /**
22953      * @cfg {String/Roo.Template} tpl The template to use to render the output
22954      */
22955      
22956     // private
22957     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22958     /**
22959      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22960      */
22961     listWidth: undefined,
22962     /**
22963      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22964      * mode = 'remote' or 'text' if mode = 'local')
22965      */
22966     displayField: undefined,
22967     /**
22968      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22969      * mode = 'remote' or 'value' if mode = 'local'). 
22970      * Note: use of a valueField requires the user make a selection
22971      * in order for a value to be mapped.
22972      */
22973     valueField: undefined,
22974     
22975     
22976     /**
22977      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22978      * field's data value (defaults to the underlying DOM element's name)
22979      */
22980     hiddenName: undefined,
22981     /**
22982      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22983      */
22984     listClass: '',
22985     /**
22986      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22987      */
22988     selectedClass: 'x-combo-selected',
22989     /**
22990      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22991      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22992      * which displays a downward arrow icon).
22993      */
22994     triggerClass : 'x-form-arrow-trigger',
22995     /**
22996      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22997      */
22998     shadow:'sides',
22999     /**
23000      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23001      * anchor positions (defaults to 'tl-bl')
23002      */
23003     listAlign: 'tl-bl?',
23004     /**
23005      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23006      */
23007     maxHeight: 300,
23008     /**
23009      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23010      * query specified by the allQuery config option (defaults to 'query')
23011      */
23012     triggerAction: 'query',
23013     /**
23014      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23015      * (defaults to 4, does not apply if editable = false)
23016      */
23017     minChars : 4,
23018     /**
23019      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23020      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23021      */
23022     typeAhead: false,
23023     /**
23024      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23025      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23026      */
23027     queryDelay: 500,
23028     /**
23029      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23030      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23031      */
23032     pageSize: 0,
23033     /**
23034      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23035      * when editable = true (defaults to false)
23036      */
23037     selectOnFocus:false,
23038     /**
23039      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23040      */
23041     queryParam: 'query',
23042     /**
23043      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23044      * when mode = 'remote' (defaults to 'Loading...')
23045      */
23046     loadingText: 'Loading...',
23047     /**
23048      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23049      */
23050     resizable: false,
23051     /**
23052      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23053      */
23054     handleHeight : 8,
23055     /**
23056      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23057      * traditional select (defaults to true)
23058      */
23059     editable: true,
23060     /**
23061      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23062      */
23063     allQuery: '',
23064     /**
23065      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23066      */
23067     mode: 'remote',
23068     /**
23069      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23070      * listWidth has a higher value)
23071      */
23072     minListWidth : 70,
23073     /**
23074      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23075      * allow the user to set arbitrary text into the field (defaults to false)
23076      */
23077     forceSelection:false,
23078     /**
23079      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23080      * if typeAhead = true (defaults to 250)
23081      */
23082     typeAheadDelay : 250,
23083     /**
23084      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23085      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23086      */
23087     valueNotFoundText : undefined,
23088     /**
23089      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23090      */
23091     blockFocus : false,
23092     
23093     /**
23094      * @cfg {Boolean} disableClear Disable showing of clear button.
23095      */
23096     disableClear : false,
23097     /**
23098      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23099      */
23100     alwaysQuery : false,
23101     
23102     //private
23103     addicon : false,
23104     editicon: false,
23105     
23106     // element that contains real text value.. (when hidden is used..)
23107      
23108     // private
23109     onRender : function(ct, position){
23110         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23111         if(this.hiddenName){
23112             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23113                     'before', true);
23114             this.hiddenField.value =
23115                 this.hiddenValue !== undefined ? this.hiddenValue :
23116                 this.value !== undefined ? this.value : '';
23117
23118             // prevent input submission
23119             this.el.dom.removeAttribute('name');
23120              
23121              
23122         }
23123         if(Roo.isGecko){
23124             this.el.dom.setAttribute('autocomplete', 'off');
23125         }
23126
23127         var cls = 'x-combo-list';
23128
23129         this.list = new Roo.Layer({
23130             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23131         });
23132
23133         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23134         this.list.setWidth(lw);
23135         this.list.swallowEvent('mousewheel');
23136         this.assetHeight = 0;
23137
23138         if(this.title){
23139             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23140             this.assetHeight += this.header.getHeight();
23141         }
23142
23143         this.innerList = this.list.createChild({cls:cls+'-inner'});
23144         this.innerList.on('mouseover', this.onViewOver, this);
23145         this.innerList.on('mousemove', this.onViewMove, this);
23146         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23147         
23148         if(this.allowBlank && !this.pageSize && !this.disableClear){
23149             this.footer = this.list.createChild({cls:cls+'-ft'});
23150             this.pageTb = new Roo.Toolbar(this.footer);
23151            
23152         }
23153         if(this.pageSize){
23154             this.footer = this.list.createChild({cls:cls+'-ft'});
23155             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23156                     {pageSize: this.pageSize});
23157             
23158         }
23159         
23160         if (this.pageTb && this.allowBlank && !this.disableClear) {
23161             var _this = this;
23162             this.pageTb.add(new Roo.Toolbar.Fill(), {
23163                 cls: 'x-btn-icon x-btn-clear',
23164                 text: '&#160;',
23165                 handler: function()
23166                 {
23167                     _this.collapse();
23168                     _this.clearValue();
23169                     _this.onSelect(false, -1);
23170                 }
23171             });
23172         }
23173         if (this.footer) {
23174             this.assetHeight += this.footer.getHeight();
23175         }
23176         
23177
23178         if(!this.tpl){
23179             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23180         }
23181
23182         this.view = new Roo.View(this.innerList, this.tpl, {
23183             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23184         });
23185
23186         this.view.on('click', this.onViewClick, this);
23187
23188         this.store.on('beforeload', this.onBeforeLoad, this);
23189         this.store.on('load', this.onLoad, this);
23190         this.store.on('loadexception', this.onLoadException, this);
23191
23192         if(this.resizable){
23193             this.resizer = new Roo.Resizable(this.list,  {
23194                pinned:true, handles:'se'
23195             });
23196             this.resizer.on('resize', function(r, w, h){
23197                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23198                 this.listWidth = w;
23199                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23200                 this.restrictHeight();
23201             }, this);
23202             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23203         }
23204         if(!this.editable){
23205             this.editable = true;
23206             this.setEditable(false);
23207         }  
23208         
23209         
23210         if (typeof(this.events.add.listeners) != 'undefined') {
23211             
23212             this.addicon = this.wrap.createChild(
23213                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23214        
23215             this.addicon.on('click', function(e) {
23216                 this.fireEvent('add', this);
23217             }, this);
23218         }
23219         if (typeof(this.events.edit.listeners) != 'undefined') {
23220             
23221             this.editicon = this.wrap.createChild(
23222                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23223             if (this.addicon) {
23224                 this.editicon.setStyle('margin-left', '40px');
23225             }
23226             this.editicon.on('click', function(e) {
23227                 
23228                 // we fire even  if inothing is selected..
23229                 this.fireEvent('edit', this, this.lastData );
23230                 
23231             }, this);
23232         }
23233         
23234         
23235         
23236     },
23237
23238     // private
23239     initEvents : function(){
23240         Roo.form.ComboBox.superclass.initEvents.call(this);
23241
23242         this.keyNav = new Roo.KeyNav(this.el, {
23243             "up" : function(e){
23244                 this.inKeyMode = true;
23245                 this.selectPrev();
23246             },
23247
23248             "down" : function(e){
23249                 if(!this.isExpanded()){
23250                     this.onTriggerClick();
23251                 }else{
23252                     this.inKeyMode = true;
23253                     this.selectNext();
23254                 }
23255             },
23256
23257             "enter" : function(e){
23258                 this.onViewClick();
23259                 //return true;
23260             },
23261
23262             "esc" : function(e){
23263                 this.collapse();
23264             },
23265
23266             "tab" : function(e){
23267                 this.onViewClick(false);
23268                 this.fireEvent("specialkey", this, e);
23269                 return true;
23270             },
23271
23272             scope : this,
23273
23274             doRelay : function(foo, bar, hname){
23275                 if(hname == 'down' || this.scope.isExpanded()){
23276                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23277                 }
23278                 return true;
23279             },
23280
23281             forceKeyDown: true
23282         });
23283         this.queryDelay = Math.max(this.queryDelay || 10,
23284                 this.mode == 'local' ? 10 : 250);
23285         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23286         if(this.typeAhead){
23287             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23288         }
23289         if(this.editable !== false){
23290             this.el.on("keyup", this.onKeyUp, this);
23291         }
23292         if(this.forceSelection){
23293             this.on('blur', this.doForce, this);
23294         }
23295     },
23296
23297     onDestroy : function(){
23298         if(this.view){
23299             this.view.setStore(null);
23300             this.view.el.removeAllListeners();
23301             this.view.el.remove();
23302             this.view.purgeListeners();
23303         }
23304         if(this.list){
23305             this.list.destroy();
23306         }
23307         if(this.store){
23308             this.store.un('beforeload', this.onBeforeLoad, this);
23309             this.store.un('load', this.onLoad, this);
23310             this.store.un('loadexception', this.onLoadException, this);
23311         }
23312         Roo.form.ComboBox.superclass.onDestroy.call(this);
23313     },
23314
23315     // private
23316     fireKey : function(e){
23317         if(e.isNavKeyPress() && !this.list.isVisible()){
23318             this.fireEvent("specialkey", this, e);
23319         }
23320     },
23321
23322     // private
23323     onResize: function(w, h){
23324         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23325         
23326         if(typeof w != 'number'){
23327             // we do not handle it!?!?
23328             return;
23329         }
23330         var tw = this.trigger.getWidth();
23331         tw += this.addicon ? this.addicon.getWidth() : 0;
23332         tw += this.editicon ? this.editicon.getWidth() : 0;
23333         var x = w - tw;
23334         this.el.setWidth( this.adjustWidth('input', x));
23335             
23336         this.trigger.setStyle('left', x+'px');
23337         
23338         if(this.list && this.listWidth === undefined){
23339             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23340             this.list.setWidth(lw);
23341             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23342         }
23343         
23344     
23345         
23346     },
23347
23348     /**
23349      * Allow or prevent the user from directly editing the field text.  If false is passed,
23350      * the user will only be able to select from the items defined in the dropdown list.  This method
23351      * is the runtime equivalent of setting the 'editable' config option at config time.
23352      * @param {Boolean} value True to allow the user to directly edit the field text
23353      */
23354     setEditable : function(value){
23355         if(value == this.editable){
23356             return;
23357         }
23358         this.editable = value;
23359         if(!value){
23360             this.el.dom.setAttribute('readOnly', true);
23361             this.el.on('mousedown', this.onTriggerClick,  this);
23362             this.el.addClass('x-combo-noedit');
23363         }else{
23364             this.el.dom.setAttribute('readOnly', false);
23365             this.el.un('mousedown', this.onTriggerClick,  this);
23366             this.el.removeClass('x-combo-noedit');
23367         }
23368     },
23369
23370     // private
23371     onBeforeLoad : function(){
23372         if(!this.hasFocus){
23373             return;
23374         }
23375         this.innerList.update(this.loadingText ?
23376                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23377         this.restrictHeight();
23378         this.selectedIndex = -1;
23379     },
23380
23381     // private
23382     onLoad : function(){
23383         if(!this.hasFocus){
23384             return;
23385         }
23386         if(this.store.getCount() > 0){
23387             this.expand();
23388             this.restrictHeight();
23389             if(this.lastQuery == this.allQuery){
23390                 if(this.editable){
23391                     this.el.dom.select();
23392                 }
23393                 if(!this.selectByValue(this.value, true)){
23394                     this.select(0, true);
23395                 }
23396             }else{
23397                 this.selectNext();
23398                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23399                     this.taTask.delay(this.typeAheadDelay);
23400                 }
23401             }
23402         }else{
23403             this.onEmptyResults();
23404         }
23405         //this.el.focus();
23406     },
23407     // private
23408     onLoadException : function()
23409     {
23410         this.collapse();
23411         Roo.log(this.store.reader.jsonData);
23412         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23413             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23414         }
23415         
23416         
23417     },
23418     // private
23419     onTypeAhead : function(){
23420         if(this.store.getCount() > 0){
23421             var r = this.store.getAt(0);
23422             var newValue = r.data[this.displayField];
23423             var len = newValue.length;
23424             var selStart = this.getRawValue().length;
23425             if(selStart != len){
23426                 this.setRawValue(newValue);
23427                 this.selectText(selStart, newValue.length);
23428             }
23429         }
23430     },
23431
23432     // private
23433     onSelect : function(record, index){
23434         if(this.fireEvent('beforeselect', this, record, index) !== false){
23435             this.setFromData(index > -1 ? record.data : false);
23436             this.collapse();
23437             this.fireEvent('select', this, record, index);
23438         }
23439     },
23440
23441     /**
23442      * Returns the currently selected field value or empty string if no value is set.
23443      * @return {String} value The selected value
23444      */
23445     getValue : function(){
23446         if(this.valueField){
23447             return typeof this.value != 'undefined' ? this.value : '';
23448         }
23449         return Roo.form.ComboBox.superclass.getValue.call(this);
23450     },
23451
23452     /**
23453      * Clears any text/value currently set in the field
23454      */
23455     clearValue : function(){
23456         if(this.hiddenField){
23457             this.hiddenField.value = '';
23458         }
23459         this.value = '';
23460         this.setRawValue('');
23461         this.lastSelectionText = '';
23462         
23463     },
23464
23465     /**
23466      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23467      * will be displayed in the field.  If the value does not match the data value of an existing item,
23468      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23469      * Otherwise the field will be blank (although the value will still be set).
23470      * @param {String} value The value to match
23471      */
23472     setValue : function(v){
23473         var text = v;
23474         if(this.valueField){
23475             var r = this.findRecord(this.valueField, v);
23476             if(r){
23477                 text = r.data[this.displayField];
23478             }else if(this.valueNotFoundText !== undefined){
23479                 text = this.valueNotFoundText;
23480             }
23481         }
23482         this.lastSelectionText = text;
23483         if(this.hiddenField){
23484             this.hiddenField.value = v;
23485         }
23486         Roo.form.ComboBox.superclass.setValue.call(this, text);
23487         this.value = v;
23488     },
23489     /**
23490      * @property {Object} the last set data for the element
23491      */
23492     
23493     lastData : false,
23494     /**
23495      * Sets the value of the field based on a object which is related to the record format for the store.
23496      * @param {Object} value the value to set as. or false on reset?
23497      */
23498     setFromData : function(o){
23499         var dv = ''; // display value
23500         var vv = ''; // value value..
23501         this.lastData = o;
23502         if (this.displayField) {
23503             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23504         } else {
23505             // this is an error condition!!!
23506             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23507         }
23508         
23509         if(this.valueField){
23510             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23511         }
23512         if(this.hiddenField){
23513             this.hiddenField.value = vv;
23514             
23515             this.lastSelectionText = dv;
23516             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23517             this.value = vv;
23518             return;
23519         }
23520         // no hidden field.. - we store the value in 'value', but still display
23521         // display field!!!!
23522         this.lastSelectionText = dv;
23523         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23524         this.value = vv;
23525         
23526         
23527     },
23528     // private
23529     reset : function(){
23530         // overridden so that last data is reset..
23531         this.setValue(this.resetValue);
23532         this.clearInvalid();
23533         this.lastData = false;
23534         if (this.view) {
23535             this.view.clearSelections();
23536         }
23537     },
23538     // private
23539     findRecord : function(prop, value){
23540         var record;
23541         if(this.store.getCount() > 0){
23542             this.store.each(function(r){
23543                 if(r.data[prop] == value){
23544                     record = r;
23545                     return false;
23546                 }
23547                 return true;
23548             });
23549         }
23550         return record;
23551     },
23552     
23553     getName: function()
23554     {
23555         // returns hidden if it's set..
23556         if (!this.rendered) {return ''};
23557         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23558         
23559     },
23560     // private
23561     onViewMove : function(e, t){
23562         this.inKeyMode = false;
23563     },
23564
23565     // private
23566     onViewOver : function(e, t){
23567         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23568             return;
23569         }
23570         var item = this.view.findItemFromChild(t);
23571         if(item){
23572             var index = this.view.indexOf(item);
23573             this.select(index, false);
23574         }
23575     },
23576
23577     // private
23578     onViewClick : function(doFocus)
23579     {
23580         var index = this.view.getSelectedIndexes()[0];
23581         var r = this.store.getAt(index);
23582         if(r){
23583             this.onSelect(r, index);
23584         }
23585         if(doFocus !== false && !this.blockFocus){
23586             this.el.focus();
23587         }
23588     },
23589
23590     // private
23591     restrictHeight : function(){
23592         this.innerList.dom.style.height = '';
23593         var inner = this.innerList.dom;
23594         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23595         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23596         this.list.beginUpdate();
23597         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23598         this.list.alignTo(this.el, this.listAlign);
23599         this.list.endUpdate();
23600     },
23601
23602     // private
23603     onEmptyResults : function(){
23604         this.collapse();
23605     },
23606
23607     /**
23608      * Returns true if the dropdown list is expanded, else false.
23609      */
23610     isExpanded : function(){
23611         return this.list.isVisible();
23612     },
23613
23614     /**
23615      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23616      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23617      * @param {String} value The data value of the item to select
23618      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23619      * selected item if it is not currently in view (defaults to true)
23620      * @return {Boolean} True if the value matched an item in the list, else false
23621      */
23622     selectByValue : function(v, scrollIntoView){
23623         if(v !== undefined && v !== null){
23624             var r = this.findRecord(this.valueField || this.displayField, v);
23625             if(r){
23626                 this.select(this.store.indexOf(r), scrollIntoView);
23627                 return true;
23628             }
23629         }
23630         return false;
23631     },
23632
23633     /**
23634      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23635      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23636      * @param {Number} index The zero-based index of the list item to select
23637      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23638      * selected item if it is not currently in view (defaults to true)
23639      */
23640     select : function(index, scrollIntoView){
23641         this.selectedIndex = index;
23642         this.view.select(index);
23643         if(scrollIntoView !== false){
23644             var el = this.view.getNode(index);
23645             if(el){
23646                 this.innerList.scrollChildIntoView(el, false);
23647             }
23648         }
23649     },
23650
23651     // private
23652     selectNext : function(){
23653         var ct = this.store.getCount();
23654         if(ct > 0){
23655             if(this.selectedIndex == -1){
23656                 this.select(0);
23657             }else if(this.selectedIndex < ct-1){
23658                 this.select(this.selectedIndex+1);
23659             }
23660         }
23661     },
23662
23663     // private
23664     selectPrev : function(){
23665         var ct = this.store.getCount();
23666         if(ct > 0){
23667             if(this.selectedIndex == -1){
23668                 this.select(0);
23669             }else if(this.selectedIndex != 0){
23670                 this.select(this.selectedIndex-1);
23671             }
23672         }
23673     },
23674
23675     // private
23676     onKeyUp : function(e){
23677         if(this.editable !== false && !e.isSpecialKey()){
23678             this.lastKey = e.getKey();
23679             this.dqTask.delay(this.queryDelay);
23680         }
23681     },
23682
23683     // private
23684     validateBlur : function(){
23685         return !this.list || !this.list.isVisible();   
23686     },
23687
23688     // private
23689     initQuery : function(){
23690         this.doQuery(this.getRawValue());
23691     },
23692
23693     // private
23694     doForce : function(){
23695         if(this.el.dom.value.length > 0){
23696             this.el.dom.value =
23697                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23698              
23699         }
23700     },
23701
23702     /**
23703      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23704      * query allowing the query action to be canceled if needed.
23705      * @param {String} query The SQL query to execute
23706      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23707      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23708      * saved in the current store (defaults to false)
23709      */
23710     doQuery : function(q, forceAll){
23711         if(q === undefined || q === null){
23712             q = '';
23713         }
23714         var qe = {
23715             query: q,
23716             forceAll: forceAll,
23717             combo: this,
23718             cancel:false
23719         };
23720         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23721             return false;
23722         }
23723         q = qe.query;
23724         forceAll = qe.forceAll;
23725         if(forceAll === true || (q.length >= this.minChars)){
23726             if(this.lastQuery != q || this.alwaysQuery){
23727                 this.lastQuery = q;
23728                 if(this.mode == 'local'){
23729                     this.selectedIndex = -1;
23730                     if(forceAll){
23731                         this.store.clearFilter();
23732                     }else{
23733                         this.store.filter(this.displayField, q);
23734                     }
23735                     this.onLoad();
23736                 }else{
23737                     this.store.baseParams[this.queryParam] = q;
23738                     this.store.load({
23739                         params: this.getParams(q)
23740                     });
23741                     this.expand();
23742                 }
23743             }else{
23744                 this.selectedIndex = -1;
23745                 this.onLoad();   
23746             }
23747         }
23748     },
23749
23750     // private
23751     getParams : function(q){
23752         var p = {};
23753         //p[this.queryParam] = q;
23754         if(this.pageSize){
23755             p.start = 0;
23756             p.limit = this.pageSize;
23757         }
23758         return p;
23759     },
23760
23761     /**
23762      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23763      */
23764     collapse : function(){
23765         if(!this.isExpanded()){
23766             return;
23767         }
23768         this.list.hide();
23769         Roo.get(document).un('mousedown', this.collapseIf, this);
23770         Roo.get(document).un('mousewheel', this.collapseIf, this);
23771         if (!this.editable) {
23772             Roo.get(document).un('keydown', this.listKeyPress, this);
23773         }
23774         this.fireEvent('collapse', this);
23775     },
23776
23777     // private
23778     collapseIf : function(e){
23779         if(!e.within(this.wrap) && !e.within(this.list)){
23780             this.collapse();
23781         }
23782     },
23783
23784     /**
23785      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23786      */
23787     expand : function(){
23788         if(this.isExpanded() || !this.hasFocus){
23789             return;
23790         }
23791         this.list.alignTo(this.el, this.listAlign);
23792         this.list.show();
23793         Roo.get(document).on('mousedown', this.collapseIf, this);
23794         Roo.get(document).on('mousewheel', this.collapseIf, this);
23795         if (!this.editable) {
23796             Roo.get(document).on('keydown', this.listKeyPress, this);
23797         }
23798         
23799         this.fireEvent('expand', this);
23800     },
23801
23802     // private
23803     // Implements the default empty TriggerField.onTriggerClick function
23804     onTriggerClick : function(){
23805         if(this.disabled){
23806             return;
23807         }
23808         if(this.isExpanded()){
23809             this.collapse();
23810             if (!this.blockFocus) {
23811                 this.el.focus();
23812             }
23813             
23814         }else {
23815             this.hasFocus = true;
23816             if(this.triggerAction == 'all') {
23817                 this.doQuery(this.allQuery, true);
23818             } else {
23819                 this.doQuery(this.getRawValue());
23820             }
23821             if (!this.blockFocus) {
23822                 this.el.focus();
23823             }
23824         }
23825     },
23826     listKeyPress : function(e)
23827     {
23828         //Roo.log('listkeypress');
23829         // scroll to first matching element based on key pres..
23830         if (e.isSpecialKey()) {
23831             return false;
23832         }
23833         var k = String.fromCharCode(e.getKey()).toUpperCase();
23834         //Roo.log(k);
23835         var match  = false;
23836         var csel = this.view.getSelectedNodes();
23837         var cselitem = false;
23838         if (csel.length) {
23839             var ix = this.view.indexOf(csel[0]);
23840             cselitem  = this.store.getAt(ix);
23841             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23842                 cselitem = false;
23843             }
23844             
23845         }
23846         
23847         this.store.each(function(v) { 
23848             if (cselitem) {
23849                 // start at existing selection.
23850                 if (cselitem.id == v.id) {
23851                     cselitem = false;
23852                 }
23853                 return;
23854             }
23855                 
23856             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23857                 match = this.store.indexOf(v);
23858                 return false;
23859             }
23860         }, this);
23861         
23862         if (match === false) {
23863             return true; // no more action?
23864         }
23865         // scroll to?
23866         this.view.select(match);
23867         var sn = Roo.get(this.view.getSelectedNodes()[0])
23868         sn.scrollIntoView(sn.dom.parentNode, false);
23869     }
23870
23871     /** 
23872     * @cfg {Boolean} grow 
23873     * @hide 
23874     */
23875     /** 
23876     * @cfg {Number} growMin 
23877     * @hide 
23878     */
23879     /** 
23880     * @cfg {Number} growMax 
23881     * @hide 
23882     */
23883     /**
23884      * @hide
23885      * @method autoSize
23886      */
23887 });/*
23888  * Copyright(c) 2010-2012, Roo J Solutions Limited
23889  *
23890  * Licence LGPL
23891  *
23892  */
23893
23894 /**
23895  * @class Roo.form.ComboBoxArray
23896  * @extends Roo.form.TextField
23897  * A facebook style adder... for lists of email / people / countries  etc...
23898  * pick multiple items from a combo box, and shows each one.
23899  *
23900  *  Fred [x]  Brian [x]  [Pick another |v]
23901  *
23902  *
23903  *  For this to work: it needs various extra information
23904  *    - normal combo problay has
23905  *      name, hiddenName
23906  *    + displayField, valueField
23907  *
23908  *    For our purpose...
23909  *
23910  *
23911  *   If we change from 'extends' to wrapping...
23912  *   
23913  *  
23914  *
23915  
23916  
23917  * @constructor
23918  * Create a new ComboBoxArray.
23919  * @param {Object} config Configuration options
23920  */
23921  
23922
23923 Roo.form.ComboBoxArray = function(config)
23924 {
23925     this.addEvents({
23926         /**
23927          * @event beforeremove
23928          * Fires before remove the value from the list
23929              * @param {Roo.form.ComboBoxArray} _self This combo box array
23930              * @param {Roo.form.ComboBoxArray.Item} item removed item
23931              */
23932         'beforeremove' : true,
23933         /**
23934          * @event remove
23935          * Fires when remove the value from the list
23936              * @param {Roo.form.ComboBoxArray} _self This combo box array
23937              * @param {Roo.form.ComboBoxArray.Item} item removed item
23938              */
23939         'remove' : true
23940         
23941         
23942     });
23943     
23944     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23945     
23946     this.items = new Roo.util.MixedCollection(false);
23947     
23948     // construct the child combo...
23949     
23950     
23951     
23952     
23953    
23954     
23955 }
23956
23957  
23958 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23959
23960     /**
23961      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23962      */
23963     
23964     lastData : false,
23965     
23966     // behavies liek a hiddne field
23967     inputType:      'hidden',
23968     /**
23969      * @cfg {Number} width The width of the box that displays the selected element
23970      */ 
23971     width:          300,
23972
23973     
23974     
23975     /**
23976      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
23977      */
23978     name : false,
23979     /**
23980      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
23981      */
23982     hiddenName : false,
23983     
23984     
23985     // private the array of items that are displayed..
23986     items  : false,
23987     // private - the hidden field el.
23988     hiddenEl : false,
23989     // private - the filed el..
23990     el : false,
23991     
23992     //validateValue : function() { return true; }, // all values are ok!
23993     //onAddClick: function() { },
23994     
23995     onRender : function(ct, position) 
23996     {
23997         
23998         // create the standard hidden element
23999         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24000         
24001         
24002         // give fake names to child combo;
24003         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24004         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24005         
24006         this.combo = Roo.factory(this.combo, Roo.form);
24007         this.combo.onRender(ct, position);
24008         if (typeof(this.combo.width) != 'undefined') {
24009             this.combo.onResize(this.combo.width,0);
24010         }
24011         
24012         this.combo.initEvents();
24013         
24014         // assigned so form know we need to do this..
24015         this.store          = this.combo.store;
24016         this.valueField     = this.combo.valueField;
24017         this.displayField   = this.combo.displayField ;
24018         
24019         
24020         this.combo.wrap.addClass('x-cbarray-grp');
24021         
24022         var cbwrap = this.combo.wrap.createChild(
24023             {tag: 'div', cls: 'x-cbarray-cb'},
24024             this.combo.el.dom
24025         );
24026         
24027              
24028         this.hiddenEl = this.combo.wrap.createChild({
24029             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24030         });
24031         this.el = this.combo.wrap.createChild({
24032             tag: 'input',  type:'hidden' , name: this.name, value : ''
24033         });
24034          //   this.el.dom.removeAttribute("name");
24035         
24036         
24037         this.outerWrap = this.combo.wrap;
24038         this.wrap = cbwrap;
24039         
24040         this.outerWrap.setWidth(this.width);
24041         this.outerWrap.dom.removeChild(this.el.dom);
24042         
24043         this.wrap.dom.appendChild(this.el.dom);
24044         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24045         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24046         
24047         this.combo.trigger.setStyle('position','relative');
24048         this.combo.trigger.setStyle('left', '0px');
24049         this.combo.trigger.setStyle('top', '2px');
24050         
24051         this.combo.el.setStyle('vertical-align', 'text-bottom');
24052         
24053         //this.trigger.setStyle('vertical-align', 'top');
24054         
24055         // this should use the code from combo really... on('add' ....)
24056         if (this.adder) {
24057             
24058         
24059             this.adder = this.outerWrap.createChild(
24060                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24061             var _t = this;
24062             this.adder.on('click', function(e) {
24063                 _t.fireEvent('adderclick', this, e);
24064             }, _t);
24065         }
24066         //var _t = this;
24067         //this.adder.on('click', this.onAddClick, _t);
24068         
24069         
24070         this.combo.on('select', function(cb, rec, ix) {
24071             this.addItem(rec.data);
24072             
24073             cb.setValue('');
24074             cb.el.dom.value = '';
24075             //cb.lastData = rec.data;
24076             // add to list
24077             
24078         }, this);
24079         
24080         
24081     },
24082     
24083     
24084     getName: function()
24085     {
24086         // returns hidden if it's set..
24087         if (!this.rendered) {return ''};
24088         return  this.hiddenName ? this.hiddenName : this.name;
24089         
24090     },
24091     
24092     
24093     onResize: function(w, h){
24094         
24095         return;
24096         // not sure if this is needed..
24097         //this.combo.onResize(w,h);
24098         
24099         if(typeof w != 'number'){
24100             // we do not handle it!?!?
24101             return;
24102         }
24103         var tw = this.combo.trigger.getWidth();
24104         tw += this.addicon ? this.addicon.getWidth() : 0;
24105         tw += this.editicon ? this.editicon.getWidth() : 0;
24106         var x = w - tw;
24107         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24108             
24109         this.combo.trigger.setStyle('left', '0px');
24110         
24111         if(this.list && this.listWidth === undefined){
24112             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24113             this.list.setWidth(lw);
24114             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24115         }
24116         
24117     
24118         
24119     },
24120     
24121     addItem: function(rec)
24122     {
24123         var valueField = this.combo.valueField;
24124         var displayField = this.combo.displayField;
24125         if (this.items.indexOfKey(rec[valueField]) > -1) {
24126             //console.log("GOT " + rec.data.id);
24127             return;
24128         }
24129         
24130         var x = new Roo.form.ComboBoxArray.Item({
24131             //id : rec[this.idField],
24132             data : rec,
24133             displayField : displayField ,
24134             tipField : displayField ,
24135             cb : this
24136         });
24137         // use the 
24138         this.items.add(rec[valueField],x);
24139         // add it before the element..
24140         this.updateHiddenEl();
24141         x.render(this.outerWrap, this.wrap.dom);
24142         // add the image handler..
24143     },
24144     
24145     updateHiddenEl : function()
24146     {
24147         this.validate();
24148         if (!this.hiddenEl) {
24149             return;
24150         }
24151         var ar = [];
24152         var idField = this.combo.valueField;
24153         
24154         this.items.each(function(f) {
24155             ar.push(f.data[idField]);
24156            
24157         });
24158         this.hiddenEl.dom.value = ar.join(',');
24159         this.validate();
24160     },
24161     
24162     reset : function()
24163     {
24164         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24165         this.items.each(function(f) {
24166            f.remove(); 
24167         });
24168         this.el.dom.value = '';
24169         if (this.hiddenEl) {
24170             this.hiddenEl.dom.value = '';
24171         }
24172         
24173     },
24174     getValue: function()
24175     {
24176         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24177     },
24178     setValue: function(v) // not a valid action - must use addItems..
24179     {
24180          
24181         this.reset();
24182         
24183         
24184         
24185         if (this.store.isLocal && (typeof(v) == 'string')) {
24186             // then we can use the store to find the values..
24187             // comma seperated at present.. this needs to allow JSON based encoding..
24188             this.hiddenEl.value  = v;
24189             var v_ar = [];
24190             Roo.each(v.split(','), function(k) {
24191                 Roo.log("CHECK " + this.valueField + ',' + k);
24192                 var li = this.store.query(this.valueField, k);
24193                 if (!li.length) {
24194                     return;
24195                 }
24196                 var add = {};
24197                 add[this.valueField] = k;
24198                 add[this.displayField] = li.item(0).data[this.displayField];
24199                 
24200                 this.addItem(add);
24201             }, this) 
24202              
24203         }
24204         if (typeof(v) == 'object' ) {
24205             // then let's assume it's an array of objects..
24206             Roo.each(v, function(l) {
24207                 this.addItem(l);
24208             }, this);
24209              
24210         }
24211         
24212         
24213     },
24214     setFromData: function(v)
24215     {
24216         // this recieves an object, if setValues is called.
24217         this.reset();
24218         this.el.dom.value = v[this.displayField];
24219         this.hiddenEl.dom.value = v[this.valueField];
24220         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24221             return;
24222         }
24223         var kv = v[this.valueField];
24224         var dv = v[this.displayField];
24225         kv = typeof(kv) != 'string' ? '' : kv;
24226         dv = typeof(dv) != 'string' ? '' : dv;
24227         
24228         
24229         var keys = kv.split(',');
24230         var display = dv.split(',');
24231         for (var i = 0 ; i < keys.length; i++) {
24232             
24233             add = {};
24234             add[this.valueField] = keys[i];
24235             add[this.displayField] = display[i];
24236             this.addItem(add);
24237         }
24238       
24239         
24240     },
24241     
24242     /**
24243      * Validates the combox array value
24244      * @return {Boolean} True if the value is valid, else false
24245      */
24246     validate : function(){
24247         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24248             this.clearInvalid();
24249             return true;
24250         }
24251         return false;
24252     },
24253     
24254     validateValue : function(value){
24255         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24256         
24257     },
24258     
24259     /*@
24260      * overide
24261      * 
24262      */
24263     isDirty : function() {
24264         if(this.disabled) {
24265             return false;
24266         }
24267         
24268         try {
24269             var d = Roo.decode(String(this.originalValue));
24270         } catch (e) {
24271             return String(this.getValue()) !== String(this.originalValue);
24272         }
24273         
24274         var originalValue = [];
24275         
24276         for (var i = 0; i < d.length; i++){
24277             originalValue.push(d[i][this.valueField]);
24278         }
24279         
24280         return String(this.getValue()) !== String(originalValue.join(','));
24281         
24282     }
24283     
24284 });
24285
24286
24287
24288 /**
24289  * @class Roo.form.ComboBoxArray.Item
24290  * @extends Roo.BoxComponent
24291  * A selected item in the list
24292  *  Fred [x]  Brian [x]  [Pick another |v]
24293  * 
24294  * @constructor
24295  * Create a new item.
24296  * @param {Object} config Configuration options
24297  */
24298  
24299 Roo.form.ComboBoxArray.Item = function(config) {
24300     config.id = Roo.id();
24301     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24302 }
24303
24304 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24305     data : {},
24306     cb: false,
24307     displayField : false,
24308     tipField : false,
24309     
24310     
24311     defaultAutoCreate : {
24312         tag: 'div',
24313         cls: 'x-cbarray-item',
24314         cn : [ 
24315             { tag: 'div' },
24316             {
24317                 tag: 'img',
24318                 width:16,
24319                 height : 16,
24320                 src : Roo.BLANK_IMAGE_URL ,
24321                 align: 'center'
24322             }
24323         ]
24324         
24325     },
24326     
24327  
24328     onRender : function(ct, position)
24329     {
24330         Roo.form.Field.superclass.onRender.call(this, ct, position);
24331         
24332         if(!this.el){
24333             var cfg = this.getAutoCreate();
24334             this.el = ct.createChild(cfg, position);
24335         }
24336         
24337         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24338         
24339         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24340             this.cb.renderer(this.data) :
24341             String.format('{0}',this.data[this.displayField]);
24342         
24343             
24344         this.el.child('div').dom.setAttribute('qtip',
24345                         String.format('{0}',this.data[this.tipField])
24346         );
24347         
24348         this.el.child('img').on('click', this.remove, this);
24349         
24350     },
24351    
24352     remove : function()
24353     {
24354         if(this.cb.disabled){
24355             return;
24356         }
24357         
24358         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24359             this.cb.items.remove(this);
24360             this.el.child('img').un('click', this.remove, this);
24361             this.el.remove();
24362             this.cb.updateHiddenEl();
24363
24364             this.cb.fireEvent('remove', this.cb, this);
24365         }
24366         
24367     }
24368 });/*
24369  * Based on:
24370  * Ext JS Library 1.1.1
24371  * Copyright(c) 2006-2007, Ext JS, LLC.
24372  *
24373  * Originally Released Under LGPL - original licence link has changed is not relivant.
24374  *
24375  * Fork - LGPL
24376  * <script type="text/javascript">
24377  */
24378 /**
24379  * @class Roo.form.Checkbox
24380  * @extends Roo.form.Field
24381  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24382  * @constructor
24383  * Creates a new Checkbox
24384  * @param {Object} config Configuration options
24385  */
24386 Roo.form.Checkbox = function(config){
24387     Roo.form.Checkbox.superclass.constructor.call(this, config);
24388     this.addEvents({
24389         /**
24390          * @event check
24391          * Fires when the checkbox is checked or unchecked.
24392              * @param {Roo.form.Checkbox} this This checkbox
24393              * @param {Boolean} checked The new checked value
24394              */
24395         check : true
24396     });
24397 };
24398
24399 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24400     /**
24401      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24402      */
24403     focusClass : undefined,
24404     /**
24405      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24406      */
24407     fieldClass: "x-form-field",
24408     /**
24409      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24410      */
24411     checked: false,
24412     /**
24413      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24414      * {tag: "input", type: "checkbox", autocomplete: "off"})
24415      */
24416     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24417     /**
24418      * @cfg {String} boxLabel The text that appears beside the checkbox
24419      */
24420     boxLabel : "",
24421     /**
24422      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24423      */  
24424     inputValue : '1',
24425     /**
24426      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24427      */
24428      valueOff: '0', // value when not checked..
24429
24430     actionMode : 'viewEl', 
24431     //
24432     // private
24433     itemCls : 'x-menu-check-item x-form-item',
24434     groupClass : 'x-menu-group-item',
24435     inputType : 'hidden',
24436     
24437     
24438     inSetChecked: false, // check that we are not calling self...
24439     
24440     inputElement: false, // real input element?
24441     basedOn: false, // ????
24442     
24443     isFormField: true, // not sure where this is needed!!!!
24444
24445     onResize : function(){
24446         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24447         if(!this.boxLabel){
24448             this.el.alignTo(this.wrap, 'c-c');
24449         }
24450     },
24451
24452     initEvents : function(){
24453         Roo.form.Checkbox.superclass.initEvents.call(this);
24454         this.el.on("click", this.onClick,  this);
24455         this.el.on("change", this.onClick,  this);
24456     },
24457
24458
24459     getResizeEl : function(){
24460         return this.wrap;
24461     },
24462
24463     getPositionEl : function(){
24464         return this.wrap;
24465     },
24466
24467     // private
24468     onRender : function(ct, position){
24469         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24470         /*
24471         if(this.inputValue !== undefined){
24472             this.el.dom.value = this.inputValue;
24473         }
24474         */
24475         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24476         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24477         var viewEl = this.wrap.createChild({ 
24478             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24479         this.viewEl = viewEl;   
24480         this.wrap.on('click', this.onClick,  this); 
24481         
24482         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24483         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24484         
24485         
24486         
24487         if(this.boxLabel){
24488             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24489         //    viewEl.on('click', this.onClick,  this); 
24490         }
24491         //if(this.checked){
24492             this.setChecked(this.checked);
24493         //}else{
24494             //this.checked = this.el.dom;
24495         //}
24496
24497     },
24498
24499     // private
24500     initValue : Roo.emptyFn,
24501
24502     /**
24503      * Returns the checked state of the checkbox.
24504      * @return {Boolean} True if checked, else false
24505      */
24506     getValue : function(){
24507         if(this.el){
24508             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24509         }
24510         return this.valueOff;
24511         
24512     },
24513
24514         // private
24515     onClick : function(){ 
24516         if (this.disabled) {
24517             return;
24518         }
24519         this.setChecked(!this.checked);
24520
24521         //if(this.el.dom.checked != this.checked){
24522         //    this.setValue(this.el.dom.checked);
24523        // }
24524     },
24525
24526     /**
24527      * Sets the checked state of the checkbox.
24528      * On is always based on a string comparison between inputValue and the param.
24529      * @param {Boolean/String} value - the value to set 
24530      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24531      */
24532     setValue : function(v,suppressEvent){
24533         
24534         
24535         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24536         //if(this.el && this.el.dom){
24537         //    this.el.dom.checked = this.checked;
24538         //    this.el.dom.defaultChecked = this.checked;
24539         //}
24540         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24541         //this.fireEvent("check", this, this.checked);
24542     },
24543     // private..
24544     setChecked : function(state,suppressEvent)
24545     {
24546         if (this.inSetChecked) {
24547             this.checked = state;
24548             return;
24549         }
24550         
24551     
24552         if(this.wrap){
24553             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24554         }
24555         this.checked = state;
24556         if(suppressEvent !== true){
24557             this.fireEvent('check', this, state);
24558         }
24559         this.inSetChecked = true;
24560         this.el.dom.value = state ? this.inputValue : this.valueOff;
24561         this.inSetChecked = false;
24562         
24563     },
24564     // handle setting of hidden value by some other method!!?!?
24565     setFromHidden: function()
24566     {
24567         if(!this.el){
24568             return;
24569         }
24570         //console.log("SET FROM HIDDEN");
24571         //alert('setFrom hidden');
24572         this.setValue(this.el.dom.value);
24573     },
24574     
24575     onDestroy : function()
24576     {
24577         if(this.viewEl){
24578             Roo.get(this.viewEl).remove();
24579         }
24580          
24581         Roo.form.Checkbox.superclass.onDestroy.call(this);
24582     }
24583
24584 });/*
24585  * Based on:
24586  * Ext JS Library 1.1.1
24587  * Copyright(c) 2006-2007, Ext JS, LLC.
24588  *
24589  * Originally Released Under LGPL - original licence link has changed is not relivant.
24590  *
24591  * Fork - LGPL
24592  * <script type="text/javascript">
24593  */
24594  
24595 /**
24596  * @class Roo.form.Radio
24597  * @extends Roo.form.Checkbox
24598  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24599  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24600  * @constructor
24601  * Creates a new Radio
24602  * @param {Object} config Configuration options
24603  */
24604 Roo.form.Radio = function(){
24605     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24606 };
24607 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24608     inputType: 'radio',
24609
24610     /**
24611      * If this radio is part of a group, it will return the selected value
24612      * @return {String}
24613      */
24614     getGroupValue : function(){
24615         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24616     },
24617     
24618     
24619     onRender : function(ct, position){
24620         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24621         
24622         if(this.inputValue !== undefined){
24623             this.el.dom.value = this.inputValue;
24624         }
24625          
24626         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24627         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24628         //var viewEl = this.wrap.createChild({ 
24629         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24630         //this.viewEl = viewEl;   
24631         //this.wrap.on('click', this.onClick,  this); 
24632         
24633         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24634         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24635         
24636         
24637         
24638         if(this.boxLabel){
24639             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24640         //    viewEl.on('click', this.onClick,  this); 
24641         }
24642          if(this.checked){
24643             this.el.dom.checked =   'checked' ;
24644         }
24645          
24646     } 
24647     
24648     
24649 });//<script type="text/javascript">
24650
24651 /*
24652  * Based  Ext JS Library 1.1.1
24653  * Copyright(c) 2006-2007, Ext JS, LLC.
24654  * LGPL
24655  *
24656  */
24657  
24658 /**
24659  * @class Roo.HtmlEditorCore
24660  * @extends Roo.Component
24661  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24662  *
24663  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24664  */
24665
24666 Roo.HtmlEditorCore = function(config){
24667     
24668     
24669     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24670     
24671     
24672     this.addEvents({
24673         /**
24674          * @event initialize
24675          * Fires when the editor is fully initialized (including the iframe)
24676          * @param {Roo.HtmlEditorCore} this
24677          */
24678         initialize: true,
24679         /**
24680          * @event activate
24681          * Fires when the editor is first receives the focus. Any insertion must wait
24682          * until after this event.
24683          * @param {Roo.HtmlEditorCore} this
24684          */
24685         activate: true,
24686          /**
24687          * @event beforesync
24688          * Fires before the textarea is updated with content from the editor iframe. Return false
24689          * to cancel the sync.
24690          * @param {Roo.HtmlEditorCore} this
24691          * @param {String} html
24692          */
24693         beforesync: true,
24694          /**
24695          * @event beforepush
24696          * Fires before the iframe editor is updated with content from the textarea. Return false
24697          * to cancel the push.
24698          * @param {Roo.HtmlEditorCore} this
24699          * @param {String} html
24700          */
24701         beforepush: true,
24702          /**
24703          * @event sync
24704          * Fires when the textarea is updated with content from the editor iframe.
24705          * @param {Roo.HtmlEditorCore} this
24706          * @param {String} html
24707          */
24708         sync: true,
24709          /**
24710          * @event push
24711          * Fires when the iframe editor is updated with content from the textarea.
24712          * @param {Roo.HtmlEditorCore} this
24713          * @param {String} html
24714          */
24715         push: true,
24716         
24717         /**
24718          * @event editorevent
24719          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24720          * @param {Roo.HtmlEditorCore} this
24721          */
24722         editorevent: true
24723     });
24724     
24725     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24726     
24727     // defaults : white / black...
24728     this.applyBlacklists();
24729     
24730     
24731     
24732 };
24733
24734
24735 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24736
24737
24738      /**
24739      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24740      */
24741     
24742     owner : false,
24743     
24744      /**
24745      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24746      *                        Roo.resizable.
24747      */
24748     resizable : false,
24749      /**
24750      * @cfg {Number} height (in pixels)
24751      */   
24752     height: 300,
24753    /**
24754      * @cfg {Number} width (in pixels)
24755      */   
24756     width: 500,
24757     
24758     /**
24759      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24760      * 
24761      */
24762     stylesheets: false,
24763     
24764     // id of frame..
24765     frameId: false,
24766     
24767     // private properties
24768     validationEvent : false,
24769     deferHeight: true,
24770     initialized : false,
24771     activated : false,
24772     sourceEditMode : false,
24773     onFocus : Roo.emptyFn,
24774     iframePad:3,
24775     hideMode:'offsets',
24776     
24777     clearUp: true,
24778     
24779     // blacklist + whitelisted elements..
24780     black: false,
24781     white: false,
24782      
24783     
24784
24785     /**
24786      * Protected method that will not generally be called directly. It
24787      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24788      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24789      */
24790     getDocMarkup : function(){
24791         // body styles..
24792         var st = '';
24793         Roo.log(this.stylesheets);
24794         
24795         // inherit styels from page...?? 
24796         if (this.stylesheets === false) {
24797             
24798             Roo.get(document.head).select('style').each(function(node) {
24799                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24800             });
24801             
24802             Roo.get(document.head).select('link').each(function(node) { 
24803                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24804             });
24805             
24806         } else if (!this.stylesheets.length) {
24807                 // simple..
24808                 st = '<style type="text/css">' +
24809                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24810                    '</style>';
24811         } else {
24812             Roo.each(this.stylesheets, function(s) {
24813                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24814             });
24815             
24816         }
24817         
24818         st +=  '<style type="text/css">' +
24819             'IMG { cursor: pointer } ' +
24820         '</style>';
24821
24822         
24823         return '<html><head>' + st  +
24824             //<style type="text/css">' +
24825             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24826             //'</style>' +
24827             ' </head><body class="roo-htmleditor-body"></body></html>';
24828     },
24829
24830     // private
24831     onRender : function(ct, position)
24832     {
24833         var _t = this;
24834         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24835         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24836         
24837         
24838         this.el.dom.style.border = '0 none';
24839         this.el.dom.setAttribute('tabIndex', -1);
24840         this.el.addClass('x-hidden hide');
24841         
24842         
24843         
24844         if(Roo.isIE){ // fix IE 1px bogus margin
24845             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24846         }
24847        
24848         
24849         this.frameId = Roo.id();
24850         
24851          
24852         
24853         var iframe = this.owner.wrap.createChild({
24854             tag: 'iframe',
24855             cls: 'form-control', // bootstrap..
24856             id: this.frameId,
24857             name: this.frameId,
24858             frameBorder : 'no',
24859             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24860         }, this.el
24861         );
24862         
24863         
24864         this.iframe = iframe.dom;
24865
24866          this.assignDocWin();
24867         
24868         this.doc.designMode = 'on';
24869        
24870         this.doc.open();
24871         this.doc.write(this.getDocMarkup());
24872         this.doc.close();
24873
24874         
24875         var task = { // must defer to wait for browser to be ready
24876             run : function(){
24877                 //console.log("run task?" + this.doc.readyState);
24878                 this.assignDocWin();
24879                 if(this.doc.body || this.doc.readyState == 'complete'){
24880                     try {
24881                         this.doc.designMode="on";
24882                     } catch (e) {
24883                         return;
24884                     }
24885                     Roo.TaskMgr.stop(task);
24886                     this.initEditor.defer(10, this);
24887                 }
24888             },
24889             interval : 10,
24890             duration: 10000,
24891             scope: this
24892         };
24893         Roo.TaskMgr.start(task);
24894
24895         
24896          
24897     },
24898
24899     // private
24900     onResize : function(w, h)
24901     {
24902          Roo.log('resize: ' +w + ',' + h );
24903         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24904         if(!this.iframe){
24905             return;
24906         }
24907         if(typeof w == 'number'){
24908             
24909             this.iframe.style.width = w + 'px';
24910         }
24911         if(typeof h == 'number'){
24912             
24913             this.iframe.style.height = h + 'px';
24914             if(this.doc){
24915                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24916             }
24917         }
24918         
24919     },
24920
24921     /**
24922      * Toggles the editor between standard and source edit mode.
24923      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24924      */
24925     toggleSourceEdit : function(sourceEditMode){
24926         
24927         this.sourceEditMode = sourceEditMode === true;
24928         
24929         if(this.sourceEditMode){
24930  
24931             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24932             
24933         }else{
24934             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24935             //this.iframe.className = '';
24936             this.deferFocus();
24937         }
24938         //this.setSize(this.owner.wrap.getSize());
24939         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24940     },
24941
24942     
24943   
24944
24945     /**
24946      * Protected method that will not generally be called directly. If you need/want
24947      * custom HTML cleanup, this is the method you should override.
24948      * @param {String} html The HTML to be cleaned
24949      * return {String} The cleaned HTML
24950      */
24951     cleanHtml : function(html){
24952         html = String(html);
24953         if(html.length > 5){
24954             if(Roo.isSafari){ // strip safari nonsense
24955                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24956             }
24957         }
24958         if(html == '&nbsp;'){
24959             html = '';
24960         }
24961         return html;
24962     },
24963
24964     /**
24965      * HTML Editor -> Textarea
24966      * Protected method that will not generally be called directly. Syncs the contents
24967      * of the editor iframe with the textarea.
24968      */
24969     syncValue : function(){
24970         if(this.initialized){
24971             var bd = (this.doc.body || this.doc.documentElement);
24972             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24973             var html = bd.innerHTML;
24974             if(Roo.isSafari){
24975                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24976                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24977                 if(m && m[1]){
24978                     html = '<div style="'+m[0]+'">' + html + '</div>';
24979                 }
24980             }
24981             html = this.cleanHtml(html);
24982             // fix up the special chars.. normaly like back quotes in word...
24983             // however we do not want to do this with chinese..
24984             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24985                 var cc = b.charCodeAt();
24986                 if (
24987                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24988                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24989                     (cc >= 0xf900 && cc < 0xfb00 )
24990                 ) {
24991                         return b;
24992                 }
24993                 return "&#"+cc+";" 
24994             });
24995             if(this.owner.fireEvent('beforesync', this, html) !== false){
24996                 this.el.dom.value = html;
24997                 this.owner.fireEvent('sync', this, html);
24998             }
24999         }
25000     },
25001
25002     /**
25003      * Protected method that will not generally be called directly. Pushes the value of the textarea
25004      * into the iframe editor.
25005      */
25006     pushValue : function(){
25007         if(this.initialized){
25008             var v = this.el.dom.value.trim();
25009             
25010 //            if(v.length < 1){
25011 //                v = '&#160;';
25012 //            }
25013             
25014             if(this.owner.fireEvent('beforepush', this, v) !== false){
25015                 var d = (this.doc.body || this.doc.documentElement);
25016                 d.innerHTML = v;
25017                 this.cleanUpPaste();
25018                 this.el.dom.value = d.innerHTML;
25019                 this.owner.fireEvent('push', this, v);
25020             }
25021         }
25022     },
25023
25024     // private
25025     deferFocus : function(){
25026         this.focus.defer(10, this);
25027     },
25028
25029     // doc'ed in Field
25030     focus : function(){
25031         if(this.win && !this.sourceEditMode){
25032             this.win.focus();
25033         }else{
25034             this.el.focus();
25035         }
25036     },
25037     
25038     assignDocWin: function()
25039     {
25040         var iframe = this.iframe;
25041         
25042          if(Roo.isIE){
25043             this.doc = iframe.contentWindow.document;
25044             this.win = iframe.contentWindow;
25045         } else {
25046 //            if (!Roo.get(this.frameId)) {
25047 //                return;
25048 //            }
25049 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25050 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25051             
25052             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25053                 return;
25054             }
25055             
25056             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25057             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25058         }
25059     },
25060     
25061     // private
25062     initEditor : function(){
25063         //console.log("INIT EDITOR");
25064         this.assignDocWin();
25065         
25066         
25067         
25068         this.doc.designMode="on";
25069         this.doc.open();
25070         this.doc.write(this.getDocMarkup());
25071         this.doc.close();
25072         
25073         var dbody = (this.doc.body || this.doc.documentElement);
25074         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25075         // this copies styles from the containing element into thsi one..
25076         // not sure why we need all of this..
25077         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25078         
25079         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25080         //ss['background-attachment'] = 'fixed'; // w3c
25081         dbody.bgProperties = 'fixed'; // ie
25082         //Roo.DomHelper.applyStyles(dbody, ss);
25083         Roo.EventManager.on(this.doc, {
25084             //'mousedown': this.onEditorEvent,
25085             'mouseup': this.onEditorEvent,
25086             'dblclick': this.onEditorEvent,
25087             'click': this.onEditorEvent,
25088             'keyup': this.onEditorEvent,
25089             buffer:100,
25090             scope: this
25091         });
25092         if(Roo.isGecko){
25093             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25094         }
25095         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25096             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25097         }
25098         this.initialized = true;
25099
25100         this.owner.fireEvent('initialize', this);
25101         this.pushValue();
25102     },
25103
25104     // private
25105     onDestroy : function(){
25106         
25107         
25108         
25109         if(this.rendered){
25110             
25111             //for (var i =0; i < this.toolbars.length;i++) {
25112             //    // fixme - ask toolbars for heights?
25113             //    this.toolbars[i].onDestroy();
25114            // }
25115             
25116             //this.wrap.dom.innerHTML = '';
25117             //this.wrap.remove();
25118         }
25119     },
25120
25121     // private
25122     onFirstFocus : function(){
25123         
25124         this.assignDocWin();
25125         
25126         
25127         this.activated = true;
25128          
25129     
25130         if(Roo.isGecko){ // prevent silly gecko errors
25131             this.win.focus();
25132             var s = this.win.getSelection();
25133             if(!s.focusNode || s.focusNode.nodeType != 3){
25134                 var r = s.getRangeAt(0);
25135                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25136                 r.collapse(true);
25137                 this.deferFocus();
25138             }
25139             try{
25140                 this.execCmd('useCSS', true);
25141                 this.execCmd('styleWithCSS', false);
25142             }catch(e){}
25143         }
25144         this.owner.fireEvent('activate', this);
25145     },
25146
25147     // private
25148     adjustFont: function(btn){
25149         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25150         //if(Roo.isSafari){ // safari
25151         //    adjust *= 2;
25152        // }
25153         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25154         if(Roo.isSafari){ // safari
25155             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25156             v =  (v < 10) ? 10 : v;
25157             v =  (v > 48) ? 48 : v;
25158             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25159             
25160         }
25161         
25162         
25163         v = Math.max(1, v+adjust);
25164         
25165         this.execCmd('FontSize', v  );
25166     },
25167
25168     onEditorEvent : function(e){
25169         this.owner.fireEvent('editorevent', this, e);
25170       //  this.updateToolbar();
25171         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25172     },
25173
25174     insertTag : function(tg)
25175     {
25176         // could be a bit smarter... -> wrap the current selected tRoo..
25177         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25178             
25179             range = this.createRange(this.getSelection());
25180             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25181             wrappingNode.appendChild(range.extractContents());
25182             range.insertNode(wrappingNode);
25183
25184             return;
25185             
25186             
25187             
25188         }
25189         this.execCmd("formatblock",   tg);
25190         
25191     },
25192     
25193     insertText : function(txt)
25194     {
25195         
25196         
25197         var range = this.createRange();
25198         range.deleteContents();
25199                //alert(Sender.getAttribute('label'));
25200                
25201         range.insertNode(this.doc.createTextNode(txt));
25202     } ,
25203     
25204      
25205
25206     /**
25207      * Executes a Midas editor command on the editor document and performs necessary focus and
25208      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25209      * @param {String} cmd The Midas command
25210      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25211      */
25212     relayCmd : function(cmd, value){
25213         this.win.focus();
25214         this.execCmd(cmd, value);
25215         this.owner.fireEvent('editorevent', this);
25216         //this.updateToolbar();
25217         this.owner.deferFocus();
25218     },
25219
25220     /**
25221      * Executes a Midas editor command directly on the editor document.
25222      * For visual commands, you should use {@link #relayCmd} instead.
25223      * <b>This should only be called after the editor is initialized.</b>
25224      * @param {String} cmd The Midas command
25225      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25226      */
25227     execCmd : function(cmd, value){
25228         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25229         this.syncValue();
25230     },
25231  
25232  
25233    
25234     /**
25235      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25236      * to insert tRoo.
25237      * @param {String} text | dom node.. 
25238      */
25239     insertAtCursor : function(text)
25240     {
25241         
25242         
25243         
25244         if(!this.activated){
25245             return;
25246         }
25247         /*
25248         if(Roo.isIE){
25249             this.win.focus();
25250             var r = this.doc.selection.createRange();
25251             if(r){
25252                 r.collapse(true);
25253                 r.pasteHTML(text);
25254                 this.syncValue();
25255                 this.deferFocus();
25256             
25257             }
25258             return;
25259         }
25260         */
25261         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25262             this.win.focus();
25263             
25264             
25265             // from jquery ui (MIT licenced)
25266             var range, node;
25267             var win = this.win;
25268             
25269             if (win.getSelection && win.getSelection().getRangeAt) {
25270                 range = win.getSelection().getRangeAt(0);
25271                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25272                 range.insertNode(node);
25273             } else if (win.document.selection && win.document.selection.createRange) {
25274                 // no firefox support
25275                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25276                 win.document.selection.createRange().pasteHTML(txt);
25277             } else {
25278                 // no firefox support
25279                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25280                 this.execCmd('InsertHTML', txt);
25281             } 
25282             
25283             this.syncValue();
25284             
25285             this.deferFocus();
25286         }
25287     },
25288  // private
25289     mozKeyPress : function(e){
25290         if(e.ctrlKey){
25291             var c = e.getCharCode(), cmd;
25292           
25293             if(c > 0){
25294                 c = String.fromCharCode(c).toLowerCase();
25295                 switch(c){
25296                     case 'b':
25297                         cmd = 'bold';
25298                         break;
25299                     case 'i':
25300                         cmd = 'italic';
25301                         break;
25302                     
25303                     case 'u':
25304                         cmd = 'underline';
25305                         break;
25306                     
25307                     case 'v':
25308                         this.cleanUpPaste.defer(100, this);
25309                         return;
25310                         
25311                 }
25312                 if(cmd){
25313                     this.win.focus();
25314                     this.execCmd(cmd);
25315                     this.deferFocus();
25316                     e.preventDefault();
25317                 }
25318                 
25319             }
25320         }
25321     },
25322
25323     // private
25324     fixKeys : function(){ // load time branching for fastest keydown performance
25325         if(Roo.isIE){
25326             return function(e){
25327                 var k = e.getKey(), r;
25328                 if(k == e.TAB){
25329                     e.stopEvent();
25330                     r = this.doc.selection.createRange();
25331                     if(r){
25332                         r.collapse(true);
25333                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25334                         this.deferFocus();
25335                     }
25336                     return;
25337                 }
25338                 
25339                 if(k == e.ENTER){
25340                     r = this.doc.selection.createRange();
25341                     if(r){
25342                         var target = r.parentElement();
25343                         if(!target || target.tagName.toLowerCase() != 'li'){
25344                             e.stopEvent();
25345                             r.pasteHTML('<br />');
25346                             r.collapse(false);
25347                             r.select();
25348                         }
25349                     }
25350                 }
25351                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25352                     this.cleanUpPaste.defer(100, this);
25353                     return;
25354                 }
25355                 
25356                 
25357             };
25358         }else if(Roo.isOpera){
25359             return function(e){
25360                 var k = e.getKey();
25361                 if(k == e.TAB){
25362                     e.stopEvent();
25363                     this.win.focus();
25364                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25365                     this.deferFocus();
25366                 }
25367                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25368                     this.cleanUpPaste.defer(100, this);
25369                     return;
25370                 }
25371                 
25372             };
25373         }else if(Roo.isSafari){
25374             return function(e){
25375                 var k = e.getKey();
25376                 
25377                 if(k == e.TAB){
25378                     e.stopEvent();
25379                     this.execCmd('InsertText','\t');
25380                     this.deferFocus();
25381                     return;
25382                 }
25383                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25384                     this.cleanUpPaste.defer(100, this);
25385                     return;
25386                 }
25387                 
25388              };
25389         }
25390     }(),
25391     
25392     getAllAncestors: function()
25393     {
25394         var p = this.getSelectedNode();
25395         var a = [];
25396         if (!p) {
25397             a.push(p); // push blank onto stack..
25398             p = this.getParentElement();
25399         }
25400         
25401         
25402         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25403             a.push(p);
25404             p = p.parentNode;
25405         }
25406         a.push(this.doc.body);
25407         return a;
25408     },
25409     lastSel : false,
25410     lastSelNode : false,
25411     
25412     
25413     getSelection : function() 
25414     {
25415         this.assignDocWin();
25416         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25417     },
25418     
25419     getSelectedNode: function() 
25420     {
25421         // this may only work on Gecko!!!
25422         
25423         // should we cache this!!!!
25424         
25425         
25426         
25427          
25428         var range = this.createRange(this.getSelection()).cloneRange();
25429         
25430         if (Roo.isIE) {
25431             var parent = range.parentElement();
25432             while (true) {
25433                 var testRange = range.duplicate();
25434                 testRange.moveToElementText(parent);
25435                 if (testRange.inRange(range)) {
25436                     break;
25437                 }
25438                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25439                     break;
25440                 }
25441                 parent = parent.parentElement;
25442             }
25443             return parent;
25444         }
25445         
25446         // is ancestor a text element.
25447         var ac =  range.commonAncestorContainer;
25448         if (ac.nodeType == 3) {
25449             ac = ac.parentNode;
25450         }
25451         
25452         var ar = ac.childNodes;
25453          
25454         var nodes = [];
25455         var other_nodes = [];
25456         var has_other_nodes = false;
25457         for (var i=0;i<ar.length;i++) {
25458             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25459                 continue;
25460             }
25461             // fullly contained node.
25462             
25463             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25464                 nodes.push(ar[i]);
25465                 continue;
25466             }
25467             
25468             // probably selected..
25469             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25470                 other_nodes.push(ar[i]);
25471                 continue;
25472             }
25473             // outer..
25474             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25475                 continue;
25476             }
25477             
25478             
25479             has_other_nodes = true;
25480         }
25481         if (!nodes.length && other_nodes.length) {
25482             nodes= other_nodes;
25483         }
25484         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25485             return false;
25486         }
25487         
25488         return nodes[0];
25489     },
25490     createRange: function(sel)
25491     {
25492         // this has strange effects when using with 
25493         // top toolbar - not sure if it's a great idea.
25494         //this.editor.contentWindow.focus();
25495         if (typeof sel != "undefined") {
25496             try {
25497                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25498             } catch(e) {
25499                 return this.doc.createRange();
25500             }
25501         } else {
25502             return this.doc.createRange();
25503         }
25504     },
25505     getParentElement: function()
25506     {
25507         
25508         this.assignDocWin();
25509         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25510         
25511         var range = this.createRange(sel);
25512          
25513         try {
25514             var p = range.commonAncestorContainer;
25515             while (p.nodeType == 3) { // text node
25516                 p = p.parentNode;
25517             }
25518             return p;
25519         } catch (e) {
25520             return null;
25521         }
25522     
25523     },
25524     /***
25525      *
25526      * Range intersection.. the hard stuff...
25527      *  '-1' = before
25528      *  '0' = hits..
25529      *  '1' = after.
25530      *         [ -- selected range --- ]
25531      *   [fail]                        [fail]
25532      *
25533      *    basically..
25534      *      if end is before start or  hits it. fail.
25535      *      if start is after end or hits it fail.
25536      *
25537      *   if either hits (but other is outside. - then it's not 
25538      *   
25539      *    
25540      **/
25541     
25542     
25543     // @see http://www.thismuchiknow.co.uk/?p=64.
25544     rangeIntersectsNode : function(range, node)
25545     {
25546         var nodeRange = node.ownerDocument.createRange();
25547         try {
25548             nodeRange.selectNode(node);
25549         } catch (e) {
25550             nodeRange.selectNodeContents(node);
25551         }
25552     
25553         var rangeStartRange = range.cloneRange();
25554         rangeStartRange.collapse(true);
25555     
25556         var rangeEndRange = range.cloneRange();
25557         rangeEndRange.collapse(false);
25558     
25559         var nodeStartRange = nodeRange.cloneRange();
25560         nodeStartRange.collapse(true);
25561     
25562         var nodeEndRange = nodeRange.cloneRange();
25563         nodeEndRange.collapse(false);
25564     
25565         return rangeStartRange.compareBoundaryPoints(
25566                  Range.START_TO_START, nodeEndRange) == -1 &&
25567                rangeEndRange.compareBoundaryPoints(
25568                  Range.START_TO_START, nodeStartRange) == 1;
25569         
25570          
25571     },
25572     rangeCompareNode : function(range, node)
25573     {
25574         var nodeRange = node.ownerDocument.createRange();
25575         try {
25576             nodeRange.selectNode(node);
25577         } catch (e) {
25578             nodeRange.selectNodeContents(node);
25579         }
25580         
25581         
25582         range.collapse(true);
25583     
25584         nodeRange.collapse(true);
25585      
25586         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25587         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25588          
25589         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25590         
25591         var nodeIsBefore   =  ss == 1;
25592         var nodeIsAfter    = ee == -1;
25593         
25594         if (nodeIsBefore && nodeIsAfter)
25595             return 0; // outer
25596         if (!nodeIsBefore && nodeIsAfter)
25597             return 1; //right trailed.
25598         
25599         if (nodeIsBefore && !nodeIsAfter)
25600             return 2;  // left trailed.
25601         // fully contined.
25602         return 3;
25603     },
25604
25605     // private? - in a new class?
25606     cleanUpPaste :  function()
25607     {
25608         // cleans up the whole document..
25609         Roo.log('cleanuppaste');
25610         
25611         this.cleanUpChildren(this.doc.body);
25612         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25613         if (clean != this.doc.body.innerHTML) {
25614             this.doc.body.innerHTML = clean;
25615         }
25616         
25617     },
25618     
25619     cleanWordChars : function(input) {// change the chars to hex code
25620         var he = Roo.HtmlEditorCore;
25621         
25622         var output = input;
25623         Roo.each(he.swapCodes, function(sw) { 
25624             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25625             
25626             output = output.replace(swapper, sw[1]);
25627         });
25628         
25629         return output;
25630     },
25631     
25632     
25633     cleanUpChildren : function (n)
25634     {
25635         if (!n.childNodes.length) {
25636             return;
25637         }
25638         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25639            this.cleanUpChild(n.childNodes[i]);
25640         }
25641     },
25642     
25643     
25644         
25645     
25646     cleanUpChild : function (node)
25647     {
25648         var ed = this;
25649         //console.log(node);
25650         if (node.nodeName == "#text") {
25651             // clean up silly Windows -- stuff?
25652             return; 
25653         }
25654         if (node.nodeName == "#comment") {
25655             node.parentNode.removeChild(node);
25656             // clean up silly Windows -- stuff?
25657             return; 
25658         }
25659         var lcname = node.tagName.toLowerCase();
25660         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25661         // whitelist of tags..
25662         
25663         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25664             // remove node.
25665             node.parentNode.removeChild(node);
25666             return;
25667             
25668         }
25669         
25670         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25671         
25672         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25673         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25674         
25675         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25676         //    remove_keep_children = true;
25677         //}
25678         
25679         if (remove_keep_children) {
25680             this.cleanUpChildren(node);
25681             // inserts everything just before this node...
25682             while (node.childNodes.length) {
25683                 var cn = node.childNodes[0];
25684                 node.removeChild(cn);
25685                 node.parentNode.insertBefore(cn, node);
25686             }
25687             node.parentNode.removeChild(node);
25688             return;
25689         }
25690         
25691         if (!node.attributes || !node.attributes.length) {
25692             this.cleanUpChildren(node);
25693             return;
25694         }
25695         
25696         function cleanAttr(n,v)
25697         {
25698             
25699             if (v.match(/^\./) || v.match(/^\//)) {
25700                 return;
25701             }
25702             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25703                 return;
25704             }
25705             if (v.match(/^#/)) {
25706                 return;
25707             }
25708 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25709             node.removeAttribute(n);
25710             
25711         }
25712         
25713         var cwhite = this.cwhite;
25714         var cblack = this.cblack;
25715             
25716         function cleanStyle(n,v)
25717         {
25718             if (v.match(/expression/)) { //XSS?? should we even bother..
25719                 node.removeAttribute(n);
25720                 return;
25721             }
25722             
25723             var parts = v.split(/;/);
25724             var clean = [];
25725             
25726             Roo.each(parts, function(p) {
25727                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25728                 if (!p.length) {
25729                     return true;
25730                 }
25731                 var l = p.split(':').shift().replace(/\s+/g,'');
25732                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25733                 
25734                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25735 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25736                     //node.removeAttribute(n);
25737                     return true;
25738                 }
25739                 //Roo.log()
25740                 // only allow 'c whitelisted system attributes'
25741                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25742 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25743                     //node.removeAttribute(n);
25744                     return true;
25745                 }
25746                 
25747                 
25748                  
25749                 
25750                 clean.push(p);
25751                 return true;
25752             });
25753             if (clean.length) { 
25754                 node.setAttribute(n, clean.join(';'));
25755             } else {
25756                 node.removeAttribute(n);
25757             }
25758             
25759         }
25760         
25761         
25762         for (var i = node.attributes.length-1; i > -1 ; i--) {
25763             var a = node.attributes[i];
25764             //console.log(a);
25765             
25766             if (a.name.toLowerCase().substr(0,2)=='on')  {
25767                 node.removeAttribute(a.name);
25768                 continue;
25769             }
25770             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25771                 node.removeAttribute(a.name);
25772                 continue;
25773             }
25774             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25775                 cleanAttr(a.name,a.value); // fixme..
25776                 continue;
25777             }
25778             if (a.name == 'style') {
25779                 cleanStyle(a.name,a.value);
25780                 continue;
25781             }
25782             /// clean up MS crap..
25783             // tecnically this should be a list of valid class'es..
25784             
25785             
25786             if (a.name == 'class') {
25787                 if (a.value.match(/^Mso/)) {
25788                     node.className = '';
25789                 }
25790                 
25791                 if (a.value.match(/body/)) {
25792                     node.className = '';
25793                 }
25794                 continue;
25795             }
25796             
25797             // style cleanup!?
25798             // class cleanup?
25799             
25800         }
25801         
25802         
25803         this.cleanUpChildren(node);
25804         
25805         
25806     },
25807     /**
25808      * Clean up MS wordisms...
25809      */
25810     cleanWord : function(node)
25811     {
25812         var _t = this;
25813         var cleanWordChildren = function()
25814         {
25815             if (!node.childNodes.length) {
25816                 return;
25817             }
25818             for (var i = node.childNodes.length-1; i > -1 ; i--) {
25819                _t.cleanWord(node.childNodes[i]);
25820             }
25821         }
25822         
25823         
25824         if (!node) {
25825             this.cleanWord(this.doc.body);
25826             return;
25827         }
25828         if (node.nodeName == "#text") {
25829             // clean up silly Windows -- stuff?
25830             return; 
25831         }
25832         if (node.nodeName == "#comment") {
25833             node.parentNode.removeChild(node);
25834             // clean up silly Windows -- stuff?
25835             return; 
25836         }
25837         
25838         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25839             node.parentNode.removeChild(node);
25840             return;
25841         }
25842         
25843         // remove - but keep children..
25844         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25845             while (node.childNodes.length) {
25846                 var cn = node.childNodes[0];
25847                 node.removeChild(cn);
25848                 node.parentNode.insertBefore(cn, node);
25849             }
25850             node.parentNode.removeChild(node);
25851             cleanWordChildren();
25852             return;
25853         }
25854         // clean styles
25855         if (node.className.length) {
25856             
25857             var cn = node.className.split(/\W+/);
25858             var cna = [];
25859             Roo.each(cn, function(cls) {
25860                 if (cls.match(/Mso[a-zA-Z]+/)) {
25861                     return;
25862                 }
25863                 cna.push(cls);
25864             });
25865             node.className = cna.length ? cna.join(' ') : '';
25866             if (!cna.length) {
25867                 node.removeAttribute("class");
25868             }
25869         }
25870         
25871         if (node.hasAttribute("lang")) {
25872             node.removeAttribute("lang");
25873         }
25874         
25875         if (node.hasAttribute("style")) {
25876             
25877             var styles = node.getAttribute("style").split(";");
25878             var nstyle = [];
25879             Roo.each(styles, function(s) {
25880                 if (!s.match(/:/)) {
25881                     return;
25882                 }
25883                 var kv = s.split(":");
25884                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25885                     return;
25886                 }
25887                 // what ever is left... we allow.
25888                 nstyle.push(s);
25889             });
25890             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25891             if (!nstyle.length) {
25892                 node.removeAttribute('style');
25893             }
25894         }
25895         
25896         cleanWordChildren();
25897         
25898         
25899     },
25900     domToHTML : function(currentElement, depth, nopadtext) {
25901         
25902         depth = depth || 0;
25903         nopadtext = nopadtext || false;
25904     
25905         if (!currentElement) {
25906             return this.domToHTML(this.doc.body);
25907         }
25908         
25909         //Roo.log(currentElement);
25910         var j;
25911         var allText = false;
25912         var nodeName = currentElement.nodeName;
25913         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25914         
25915         if  (nodeName == '#text') {
25916             return currentElement.nodeValue;
25917         }
25918         
25919         
25920         var ret = '';
25921         if (nodeName != 'BODY') {
25922              
25923             var i = 0;
25924             // Prints the node tagName, such as <A>, <IMG>, etc
25925             if (tagName) {
25926                 var attr = [];
25927                 for(i = 0; i < currentElement.attributes.length;i++) {
25928                     // quoting?
25929                     var aname = currentElement.attributes.item(i).name;
25930                     if (!currentElement.attributes.item(i).value.length) {
25931                         continue;
25932                     }
25933                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25934                 }
25935                 
25936                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25937             } 
25938             else {
25939                 
25940                 // eack
25941             }
25942         } else {
25943             tagName = false;
25944         }
25945         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25946             return ret;
25947         }
25948         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25949             nopadtext = true;
25950         }
25951         
25952         
25953         // Traverse the tree
25954         i = 0;
25955         var currentElementChild = currentElement.childNodes.item(i);
25956         var allText = true;
25957         var innerHTML  = '';
25958         lastnode = '';
25959         while (currentElementChild) {
25960             // Formatting code (indent the tree so it looks nice on the screen)
25961             var nopad = nopadtext;
25962             if (lastnode == 'SPAN') {
25963                 nopad  = true;
25964             }
25965             // text
25966             if  (currentElementChild.nodeName == '#text') {
25967                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25968                 if (!nopad && toadd.length > 80) {
25969                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25970                 }
25971                 innerHTML  += toadd;
25972                 
25973                 i++;
25974                 currentElementChild = currentElement.childNodes.item(i);
25975                 lastNode = '';
25976                 continue;
25977             }
25978             allText = false;
25979             
25980             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25981                 
25982             // Recursively traverse the tree structure of the child node
25983             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25984             lastnode = currentElementChild.nodeName;
25985             i++;
25986             currentElementChild=currentElement.childNodes.item(i);
25987         }
25988         
25989         ret += innerHTML;
25990         
25991         if (!allText) {
25992                 // The remaining code is mostly for formatting the tree
25993             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25994         }
25995         
25996         
25997         if (tagName) {
25998             ret+= "</"+tagName+">";
25999         }
26000         return ret;
26001         
26002     },
26003         
26004     applyBlacklists : function()
26005     {
26006         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26007         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26008         
26009         this.white = [];
26010         this.black = [];
26011         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26012             if (b.indexOf(tag) > -1) {
26013                 return;
26014             }
26015             this.white.push(tag);
26016             
26017         }, this);
26018         
26019         Roo.each(w, function(tag) {
26020             if (b.indexOf(tag) > -1) {
26021                 return;
26022             }
26023             if (this.white.indexOf(tag) > -1) {
26024                 return;
26025             }
26026             this.white.push(tag);
26027             
26028         }, this);
26029         
26030         
26031         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26032             if (w.indexOf(tag) > -1) {
26033                 return;
26034             }
26035             this.black.push(tag);
26036             
26037         }, this);
26038         
26039         Roo.each(b, function(tag) {
26040             if (w.indexOf(tag) > -1) {
26041                 return;
26042             }
26043             if (this.black.indexOf(tag) > -1) {
26044                 return;
26045             }
26046             this.black.push(tag);
26047             
26048         }, this);
26049         
26050         
26051         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26052         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26053         
26054         this.cwhite = [];
26055         this.cblack = [];
26056         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26057             if (b.indexOf(tag) > -1) {
26058                 return;
26059             }
26060             this.cwhite.push(tag);
26061             
26062         }, this);
26063         
26064         Roo.each(w, function(tag) {
26065             if (b.indexOf(tag) > -1) {
26066                 return;
26067             }
26068             if (this.cwhite.indexOf(tag) > -1) {
26069                 return;
26070             }
26071             this.cwhite.push(tag);
26072             
26073         }, this);
26074         
26075         
26076         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26077             if (w.indexOf(tag) > -1) {
26078                 return;
26079             }
26080             this.cblack.push(tag);
26081             
26082         }, this);
26083         
26084         Roo.each(b, function(tag) {
26085             if (w.indexOf(tag) > -1) {
26086                 return;
26087             }
26088             if (this.cblack.indexOf(tag) > -1) {
26089                 return;
26090             }
26091             this.cblack.push(tag);
26092             
26093         }, this);
26094     }
26095     
26096     // hide stuff that is not compatible
26097     /**
26098      * @event blur
26099      * @hide
26100      */
26101     /**
26102      * @event change
26103      * @hide
26104      */
26105     /**
26106      * @event focus
26107      * @hide
26108      */
26109     /**
26110      * @event specialkey
26111      * @hide
26112      */
26113     /**
26114      * @cfg {String} fieldClass @hide
26115      */
26116     /**
26117      * @cfg {String} focusClass @hide
26118      */
26119     /**
26120      * @cfg {String} autoCreate @hide
26121      */
26122     /**
26123      * @cfg {String} inputType @hide
26124      */
26125     /**
26126      * @cfg {String} invalidClass @hide
26127      */
26128     /**
26129      * @cfg {String} invalidText @hide
26130      */
26131     /**
26132      * @cfg {String} msgFx @hide
26133      */
26134     /**
26135      * @cfg {String} validateOnBlur @hide
26136      */
26137 });
26138
26139 Roo.HtmlEditorCore.white = [
26140         'area', 'br', 'img', 'input', 'hr', 'wbr',
26141         
26142        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26143        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26144        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26145        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26146        'table',   'ul',         'xmp', 
26147        
26148        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26149       'thead',   'tr', 
26150      
26151       'dir', 'menu', 'ol', 'ul', 'dl',
26152        
26153       'embed',  'object'
26154 ];
26155
26156
26157 Roo.HtmlEditorCore.black = [
26158     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26159         'applet', // 
26160         'base',   'basefont', 'bgsound', 'blink',  'body', 
26161         'frame',  'frameset', 'head',    'html',   'ilayer', 
26162         'iframe', 'layer',  'link',     'meta',    'object',   
26163         'script', 'style' ,'title',  'xml' // clean later..
26164 ];
26165 Roo.HtmlEditorCore.clean = [
26166     'script', 'style', 'title', 'xml'
26167 ];
26168 Roo.HtmlEditorCore.remove = [
26169     'font'
26170 ];
26171 // attributes..
26172
26173 Roo.HtmlEditorCore.ablack = [
26174     'on'
26175 ];
26176     
26177 Roo.HtmlEditorCore.aclean = [ 
26178     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26179 ];
26180
26181 // protocols..
26182 Roo.HtmlEditorCore.pwhite= [
26183         'http',  'https',  'mailto'
26184 ];
26185
26186 // white listed style attributes.
26187 Roo.HtmlEditorCore.cwhite= [
26188       //  'text-align', /// default is to allow most things..
26189       
26190          
26191 //        'font-size'//??
26192 ];
26193
26194 // black listed style attributes.
26195 Roo.HtmlEditorCore.cblack= [
26196       //  'font-size' -- this can be set by the project 
26197 ];
26198
26199
26200 Roo.HtmlEditorCore.swapCodes   =[ 
26201     [    8211, "--" ], 
26202     [    8212, "--" ], 
26203     [    8216,  "'" ],  
26204     [    8217, "'" ],  
26205     [    8220, '"' ],  
26206     [    8221, '"' ],  
26207     [    8226, "*" ],  
26208     [    8230, "..." ]
26209 ]; 
26210
26211     //<script type="text/javascript">
26212
26213 /*
26214  * Ext JS Library 1.1.1
26215  * Copyright(c) 2006-2007, Ext JS, LLC.
26216  * Licence LGPL
26217  * 
26218  */
26219  
26220  
26221 Roo.form.HtmlEditor = function(config){
26222     
26223     
26224     
26225     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26226     
26227     if (!this.toolbars) {
26228         this.toolbars = [];
26229     }
26230     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26231     
26232     
26233 };
26234
26235 /**
26236  * @class Roo.form.HtmlEditor
26237  * @extends Roo.form.Field
26238  * Provides a lightweight HTML Editor component.
26239  *
26240  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26241  * 
26242  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26243  * supported by this editor.</b><br/><br/>
26244  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26245  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26246  */
26247 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26248     /**
26249      * @cfg {Boolean} clearUp
26250      */
26251     clearUp : true,
26252       /**
26253      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26254      */
26255     toolbars : false,
26256    
26257      /**
26258      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26259      *                        Roo.resizable.
26260      */
26261     resizable : false,
26262      /**
26263      * @cfg {Number} height (in pixels)
26264      */   
26265     height: 300,
26266    /**
26267      * @cfg {Number} width (in pixels)
26268      */   
26269     width: 500,
26270     
26271     /**
26272      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26273      * 
26274      */
26275     stylesheets: false,
26276     
26277     
26278      /**
26279      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26280      * 
26281      */
26282     cblack: false,
26283     /**
26284      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26285      * 
26286      */
26287     cwhite: false,
26288     
26289      /**
26290      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26291      * 
26292      */
26293     black: false,
26294     /**
26295      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26296      * 
26297      */
26298     white: false,
26299     
26300     // id of frame..
26301     frameId: false,
26302     
26303     // private properties
26304     validationEvent : false,
26305     deferHeight: true,
26306     initialized : false,
26307     activated : false,
26308     
26309     onFocus : Roo.emptyFn,
26310     iframePad:3,
26311     hideMode:'offsets',
26312     
26313     actionMode : 'container', // defaults to hiding it...
26314     
26315     defaultAutoCreate : { // modified by initCompnoent..
26316         tag: "textarea",
26317         style:"width:500px;height:300px;",
26318         autocomplete: "off"
26319     },
26320
26321     // private
26322     initComponent : function(){
26323         this.addEvents({
26324             /**
26325              * @event initialize
26326              * Fires when the editor is fully initialized (including the iframe)
26327              * @param {HtmlEditor} this
26328              */
26329             initialize: true,
26330             /**
26331              * @event activate
26332              * Fires when the editor is first receives the focus. Any insertion must wait
26333              * until after this event.
26334              * @param {HtmlEditor} this
26335              */
26336             activate: true,
26337              /**
26338              * @event beforesync
26339              * Fires before the textarea is updated with content from the editor iframe. Return false
26340              * to cancel the sync.
26341              * @param {HtmlEditor} this
26342              * @param {String} html
26343              */
26344             beforesync: true,
26345              /**
26346              * @event beforepush
26347              * Fires before the iframe editor is updated with content from the textarea. Return false
26348              * to cancel the push.
26349              * @param {HtmlEditor} this
26350              * @param {String} html
26351              */
26352             beforepush: true,
26353              /**
26354              * @event sync
26355              * Fires when the textarea is updated with content from the editor iframe.
26356              * @param {HtmlEditor} this
26357              * @param {String} html
26358              */
26359             sync: true,
26360              /**
26361              * @event push
26362              * Fires when the iframe editor is updated with content from the textarea.
26363              * @param {HtmlEditor} this
26364              * @param {String} html
26365              */
26366             push: true,
26367              /**
26368              * @event editmodechange
26369              * Fires when the editor switches edit modes
26370              * @param {HtmlEditor} this
26371              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26372              */
26373             editmodechange: true,
26374             /**
26375              * @event editorevent
26376              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26377              * @param {HtmlEditor} this
26378              */
26379             editorevent: true,
26380             /**
26381              * @event firstfocus
26382              * Fires when on first focus - needed by toolbars..
26383              * @param {HtmlEditor} this
26384              */
26385             firstfocus: true,
26386             /**
26387              * @event autosave
26388              * Auto save the htmlEditor value as a file into Events
26389              * @param {HtmlEditor} this
26390              */
26391             autosave: true,
26392             /**
26393              * @event savedpreview
26394              * preview the saved version of htmlEditor
26395              * @param {HtmlEditor} this
26396              */
26397             savedpreview: true
26398         });
26399         this.defaultAutoCreate =  {
26400             tag: "textarea",
26401             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26402             autocomplete: "off"
26403         };
26404     },
26405
26406     /**
26407      * Protected method that will not generally be called directly. It
26408      * is called when the editor creates its toolbar. Override this method if you need to
26409      * add custom toolbar buttons.
26410      * @param {HtmlEditor} editor
26411      */
26412     createToolbar : function(editor){
26413         Roo.log("create toolbars");
26414         if (!editor.toolbars || !editor.toolbars.length) {
26415             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26416         }
26417         
26418         for (var i =0 ; i < editor.toolbars.length;i++) {
26419             editor.toolbars[i] = Roo.factory(
26420                     typeof(editor.toolbars[i]) == 'string' ?
26421                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26422                 Roo.form.HtmlEditor);
26423             editor.toolbars[i].init(editor);
26424         }
26425          
26426         
26427     },
26428
26429      
26430     // private
26431     onRender : function(ct, position)
26432     {
26433         var _t = this;
26434         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26435         
26436         this.wrap = this.el.wrap({
26437             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26438         });
26439         
26440         this.editorcore.onRender(ct, position);
26441          
26442         if (this.resizable) {
26443             this.resizeEl = new Roo.Resizable(this.wrap, {
26444                 pinned : true,
26445                 wrap: true,
26446                 dynamic : true,
26447                 minHeight : this.height,
26448                 height: this.height,
26449                 handles : this.resizable,
26450                 width: this.width,
26451                 listeners : {
26452                     resize : function(r, w, h) {
26453                         _t.onResize(w,h); // -something
26454                     }
26455                 }
26456             });
26457             
26458         }
26459         this.createToolbar(this);
26460        
26461         
26462         if(!this.width){
26463             this.setSize(this.wrap.getSize());
26464         }
26465         if (this.resizeEl) {
26466             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26467             // should trigger onReize..
26468         }
26469         
26470 //        if(this.autosave && this.w){
26471 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26472 //        }
26473     },
26474
26475     // private
26476     onResize : function(w, h)
26477     {
26478         //Roo.log('resize: ' +w + ',' + h );
26479         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26480         var ew = false;
26481         var eh = false;
26482         
26483         if(this.el ){
26484             if(typeof w == 'number'){
26485                 var aw = w - this.wrap.getFrameWidth('lr');
26486                 this.el.setWidth(this.adjustWidth('textarea', aw));
26487                 ew = aw;
26488             }
26489             if(typeof h == 'number'){
26490                 var tbh = 0;
26491                 for (var i =0; i < this.toolbars.length;i++) {
26492                     // fixme - ask toolbars for heights?
26493                     tbh += this.toolbars[i].tb.el.getHeight();
26494                     if (this.toolbars[i].footer) {
26495                         tbh += this.toolbars[i].footer.el.getHeight();
26496                     }
26497                 }
26498                 
26499                 
26500                 
26501                 
26502                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26503                 ah -= 5; // knock a few pixes off for look..
26504                 this.el.setHeight(this.adjustWidth('textarea', ah));
26505                 var eh = ah;
26506             }
26507         }
26508         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26509         this.editorcore.onResize(ew,eh);
26510         
26511     },
26512
26513     /**
26514      * Toggles the editor between standard and source edit mode.
26515      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26516      */
26517     toggleSourceEdit : function(sourceEditMode)
26518     {
26519         this.editorcore.toggleSourceEdit(sourceEditMode);
26520         
26521         if(this.editorcore.sourceEditMode){
26522             Roo.log('editor - showing textarea');
26523             
26524 //            Roo.log('in');
26525 //            Roo.log(this.syncValue());
26526             this.editorcore.syncValue();
26527             this.el.removeClass('x-hidden');
26528             this.el.dom.removeAttribute('tabIndex');
26529             this.el.focus();
26530         }else{
26531             Roo.log('editor - hiding textarea');
26532 //            Roo.log('out')
26533 //            Roo.log(this.pushValue()); 
26534             this.editorcore.pushValue();
26535             
26536             this.el.addClass('x-hidden');
26537             this.el.dom.setAttribute('tabIndex', -1);
26538             //this.deferFocus();
26539         }
26540          
26541         this.setSize(this.wrap.getSize());
26542         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26543     },
26544  
26545     // private (for BoxComponent)
26546     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26547
26548     // private (for BoxComponent)
26549     getResizeEl : function(){
26550         return this.wrap;
26551     },
26552
26553     // private (for BoxComponent)
26554     getPositionEl : function(){
26555         return this.wrap;
26556     },
26557
26558     // private
26559     initEvents : function(){
26560         this.originalValue = this.getValue();
26561     },
26562
26563     /**
26564      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26565      * @method
26566      */
26567     markInvalid : Roo.emptyFn,
26568     /**
26569      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26570      * @method
26571      */
26572     clearInvalid : Roo.emptyFn,
26573
26574     setValue : function(v){
26575         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26576         this.editorcore.pushValue();
26577     },
26578
26579      
26580     // private
26581     deferFocus : function(){
26582         this.focus.defer(10, this);
26583     },
26584
26585     // doc'ed in Field
26586     focus : function(){
26587         this.editorcore.focus();
26588         
26589     },
26590       
26591
26592     // private
26593     onDestroy : function(){
26594         
26595         
26596         
26597         if(this.rendered){
26598             
26599             for (var i =0; i < this.toolbars.length;i++) {
26600                 // fixme - ask toolbars for heights?
26601                 this.toolbars[i].onDestroy();
26602             }
26603             
26604             this.wrap.dom.innerHTML = '';
26605             this.wrap.remove();
26606         }
26607     },
26608
26609     // private
26610     onFirstFocus : function(){
26611         //Roo.log("onFirstFocus");
26612         this.editorcore.onFirstFocus();
26613          for (var i =0; i < this.toolbars.length;i++) {
26614             this.toolbars[i].onFirstFocus();
26615         }
26616         
26617     },
26618     
26619     // private
26620     syncValue : function()
26621     {
26622         this.editorcore.syncValue();
26623     },
26624     
26625     pushValue : function()
26626     {
26627         this.editorcore.pushValue();
26628     }
26629      
26630     
26631     // hide stuff that is not compatible
26632     /**
26633      * @event blur
26634      * @hide
26635      */
26636     /**
26637      * @event change
26638      * @hide
26639      */
26640     /**
26641      * @event focus
26642      * @hide
26643      */
26644     /**
26645      * @event specialkey
26646      * @hide
26647      */
26648     /**
26649      * @cfg {String} fieldClass @hide
26650      */
26651     /**
26652      * @cfg {String} focusClass @hide
26653      */
26654     /**
26655      * @cfg {String} autoCreate @hide
26656      */
26657     /**
26658      * @cfg {String} inputType @hide
26659      */
26660     /**
26661      * @cfg {String} invalidClass @hide
26662      */
26663     /**
26664      * @cfg {String} invalidText @hide
26665      */
26666     /**
26667      * @cfg {String} msgFx @hide
26668      */
26669     /**
26670      * @cfg {String} validateOnBlur @hide
26671      */
26672 });
26673  
26674     // <script type="text/javascript">
26675 /*
26676  * Based on
26677  * Ext JS Library 1.1.1
26678  * Copyright(c) 2006-2007, Ext JS, LLC.
26679  *  
26680  
26681  */
26682
26683 /**
26684  * @class Roo.form.HtmlEditorToolbar1
26685  * Basic Toolbar
26686  * 
26687  * Usage:
26688  *
26689  new Roo.form.HtmlEditor({
26690     ....
26691     toolbars : [
26692         new Roo.form.HtmlEditorToolbar1({
26693             disable : { fonts: 1 , format: 1, ..., ... , ...],
26694             btns : [ .... ]
26695         })
26696     }
26697      
26698  * 
26699  * @cfg {Object} disable List of elements to disable..
26700  * @cfg {Array} btns List of additional buttons.
26701  * 
26702  * 
26703  * NEEDS Extra CSS? 
26704  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26705  */
26706  
26707 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26708 {
26709     
26710     Roo.apply(this, config);
26711     
26712     // default disabled, based on 'good practice'..
26713     this.disable = this.disable || {};
26714     Roo.applyIf(this.disable, {
26715         fontSize : true,
26716         colors : true,
26717         specialElements : true
26718     });
26719     
26720     
26721     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26722     // dont call parent... till later.
26723 }
26724
26725 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26726     
26727     tb: false,
26728     
26729     rendered: false,
26730     
26731     editor : false,
26732     editorcore : false,
26733     /**
26734      * @cfg {Object} disable  List of toolbar elements to disable
26735          
26736      */
26737     disable : false,
26738     
26739     
26740      /**
26741      * @cfg {String} createLinkText The default text for the create link prompt
26742      */
26743     createLinkText : 'Please enter the URL for the link:',
26744     /**
26745      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
26746      */
26747     defaultLinkValue : 'http:/'+'/',
26748    
26749     
26750       /**
26751      * @cfg {Array} fontFamilies An array of available font families
26752      */
26753     fontFamilies : [
26754         'Arial',
26755         'Courier New',
26756         'Tahoma',
26757         'Times New Roman',
26758         'Verdana'
26759     ],
26760     
26761     specialChars : [
26762            "&#169;",
26763           "&#174;",     
26764           "&#8482;",    
26765           "&#163;" ,    
26766          // "&#8212;",    
26767           "&#8230;",    
26768           "&#247;" ,    
26769         //  "&#225;" ,     ?? a acute?
26770            "&#8364;"    , //Euro
26771        //   "&#8220;"    ,
26772         //  "&#8221;"    ,
26773         //  "&#8226;"    ,
26774           "&#176;"  //   , // degrees
26775
26776          // "&#233;"     , // e ecute
26777          // "&#250;"     , // u ecute?
26778     ],
26779     
26780     specialElements : [
26781         {
26782             text: "Insert Table",
26783             xtype: 'MenuItem',
26784             xns : Roo.Menu,
26785             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26786                 
26787         },
26788         {    
26789             text: "Insert Image",
26790             xtype: 'MenuItem',
26791             xns : Roo.Menu,
26792             ihtml : '<img src="about:blank"/>'
26793             
26794         }
26795         
26796          
26797     ],
26798     
26799     
26800     inputElements : [ 
26801             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26802             "input:submit", "input:button", "select", "textarea", "label" ],
26803     formats : [
26804         ["p"] ,  
26805         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26806         ["pre"],[ "code"], 
26807         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26808         ['div'],['span']
26809     ],
26810     
26811     cleanStyles : [
26812         "font-size"
26813     ],
26814      /**
26815      * @cfg {String} defaultFont default font to use.
26816      */
26817     defaultFont: 'tahoma',
26818    
26819     fontSelect : false,
26820     
26821     
26822     formatCombo : false,
26823     
26824     init : function(editor)
26825     {
26826         this.editor = editor;
26827         this.editorcore = editor.editorcore ? editor.editorcore : editor;
26828         var editorcore = this.editorcore;
26829         
26830         var _t = this;
26831         
26832         var fid = editorcore.frameId;
26833         var etb = this;
26834         function btn(id, toggle, handler){
26835             var xid = fid + '-'+ id ;
26836             return {
26837                 id : xid,
26838                 cmd : id,
26839                 cls : 'x-btn-icon x-edit-'+id,
26840                 enableToggle:toggle !== false,
26841                 scope: _t, // was editor...
26842                 handler:handler||_t.relayBtnCmd,
26843                 clickEvent:'mousedown',
26844                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26845                 tabIndex:-1
26846             };
26847         }
26848         
26849         
26850         
26851         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26852         this.tb = tb;
26853          // stop form submits
26854         tb.el.on('click', function(e){
26855             e.preventDefault(); // what does this do?
26856         });
26857
26858         if(!this.disable.font) { // && !Roo.isSafari){
26859             /* why no safari for fonts 
26860             editor.fontSelect = tb.el.createChild({
26861                 tag:'select',
26862                 tabIndex: -1,
26863                 cls:'x-font-select',
26864                 html: this.createFontOptions()
26865             });
26866             
26867             editor.fontSelect.on('change', function(){
26868                 var font = editor.fontSelect.dom.value;
26869                 editor.relayCmd('fontname', font);
26870                 editor.deferFocus();
26871             }, editor);
26872             
26873             tb.add(
26874                 editor.fontSelect.dom,
26875                 '-'
26876             );
26877             */
26878             
26879         };
26880         if(!this.disable.formats){
26881             this.formatCombo = new Roo.form.ComboBox({
26882                 store: new Roo.data.SimpleStore({
26883                     id : 'tag',
26884                     fields: ['tag'],
26885                     data : this.formats // from states.js
26886                 }),
26887                 blockFocus : true,
26888                 name : '',
26889                 //autoCreate : {tag: "div",  size: "20"},
26890                 displayField:'tag',
26891                 typeAhead: false,
26892                 mode: 'local',
26893                 editable : false,
26894                 triggerAction: 'all',
26895                 emptyText:'Add tag',
26896                 selectOnFocus:true,
26897                 width:135,
26898                 listeners : {
26899                     'select': function(c, r, i) {
26900                         editorcore.insertTag(r.get('tag'));
26901                         editor.focus();
26902                     }
26903                 }
26904
26905             });
26906             tb.addField(this.formatCombo);
26907             
26908         }
26909         
26910         if(!this.disable.format){
26911             tb.add(
26912                 btn('bold'),
26913                 btn('italic'),
26914                 btn('underline')
26915             );
26916         };
26917         if(!this.disable.fontSize){
26918             tb.add(
26919                 '-',
26920                 
26921                 
26922                 btn('increasefontsize', false, editorcore.adjustFont),
26923                 btn('decreasefontsize', false, editorcore.adjustFont)
26924             );
26925         };
26926         
26927         
26928         if(!this.disable.colors){
26929             tb.add(
26930                 '-', {
26931                     id:editorcore.frameId +'-forecolor',
26932                     cls:'x-btn-icon x-edit-forecolor',
26933                     clickEvent:'mousedown',
26934                     tooltip: this.buttonTips['forecolor'] || undefined,
26935                     tabIndex:-1,
26936                     menu : new Roo.menu.ColorMenu({
26937                         allowReselect: true,
26938                         focus: Roo.emptyFn,
26939                         value:'000000',
26940                         plain:true,
26941                         selectHandler: function(cp, color){
26942                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26943                             editor.deferFocus();
26944                         },
26945                         scope: editorcore,
26946                         clickEvent:'mousedown'
26947                     })
26948                 }, {
26949                     id:editorcore.frameId +'backcolor',
26950                     cls:'x-btn-icon x-edit-backcolor',
26951                     clickEvent:'mousedown',
26952                     tooltip: this.buttonTips['backcolor'] || undefined,
26953                     tabIndex:-1,
26954                     menu : new Roo.menu.ColorMenu({
26955                         focus: Roo.emptyFn,
26956                         value:'FFFFFF',
26957                         plain:true,
26958                         allowReselect: true,
26959                         selectHandler: function(cp, color){
26960                             if(Roo.isGecko){
26961                                 editorcore.execCmd('useCSS', false);
26962                                 editorcore.execCmd('hilitecolor', color);
26963                                 editorcore.execCmd('useCSS', true);
26964                                 editor.deferFocus();
26965                             }else{
26966                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26967                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26968                                 editor.deferFocus();
26969                             }
26970                         },
26971                         scope:editorcore,
26972                         clickEvent:'mousedown'
26973                     })
26974                 }
26975             );
26976         };
26977         // now add all the items...
26978         
26979
26980         if(!this.disable.alignments){
26981             tb.add(
26982                 '-',
26983                 btn('justifyleft'),
26984                 btn('justifycenter'),
26985                 btn('justifyright')
26986             );
26987         };
26988
26989         //if(!Roo.isSafari){
26990             if(!this.disable.links){
26991                 tb.add(
26992                     '-',
26993                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
26994                 );
26995             };
26996
26997             if(!this.disable.lists){
26998                 tb.add(
26999                     '-',
27000                     btn('insertorderedlist'),
27001                     btn('insertunorderedlist')
27002                 );
27003             }
27004             if(!this.disable.sourceEdit){
27005                 tb.add(
27006                     '-',
27007                     btn('sourceedit', true, function(btn){
27008                         Roo.log(this);
27009                         this.toggleSourceEdit(btn.pressed);
27010                     })
27011                 );
27012             }
27013         //}
27014         
27015         var smenu = { };
27016         // special menu.. - needs to be tidied up..
27017         if (!this.disable.special) {
27018             smenu = {
27019                 text: "&#169;",
27020                 cls: 'x-edit-none',
27021                 
27022                 menu : {
27023                     items : []
27024                 }
27025             };
27026             for (var i =0; i < this.specialChars.length; i++) {
27027                 smenu.menu.items.push({
27028                     
27029                     html: this.specialChars[i],
27030                     handler: function(a,b) {
27031                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27032                         //editor.insertAtCursor(a.html);
27033                         
27034                     },
27035                     tabIndex:-1
27036                 });
27037             }
27038             
27039             
27040             tb.add(smenu);
27041             
27042             
27043         }
27044         
27045         var cmenu = { };
27046         if (!this.disable.cleanStyles) {
27047             cmenu = {
27048                 cls: 'x-btn-icon x-btn-clear',
27049                 
27050                 menu : {
27051                     items : []
27052                 }
27053             };
27054             for (var i =0; i < this.cleanStyles.length; i++) {
27055                 cmenu.menu.items.push({
27056                     actiontype : this.cleanStyles[i],
27057                     html: 'Remove ' + this.cleanStyles[i],
27058                     handler: function(a,b) {
27059                         Roo.log(a);
27060                         Roo.log(b);
27061                         var c = Roo.get(editorcore.doc.body);
27062                         c.select('[style]').each(function(s) {
27063                             s.dom.style.removeProperty(a.actiontype);
27064                         });
27065                         editorcore.syncValue();
27066                     },
27067                     tabIndex:-1
27068                 });
27069             }
27070             cmenu.menu.items.push({
27071                 actiontype : 'word',
27072                 html: 'Remove MS Word Formating',
27073                 handler: function(a,b) {
27074                     editorcore.cleanWord();
27075                     editorcore.syncValue();
27076                 },
27077                 tabIndex:-1
27078             });
27079             
27080             cmenu.menu.items.push({
27081                 actiontype : 'all',
27082                 html: 'Remove All Styles',
27083                 handler: function(a,b) {
27084                     
27085                     var c = Roo.get(editorcore.doc.body);
27086                     c.select('[style]').each(function(s) {
27087                         s.dom.removeAttribute('style');
27088                     });
27089                     editorcore.syncValue();
27090                 },
27091                 tabIndex:-1
27092             });
27093              cmenu.menu.items.push({
27094                 actiontype : 'word',
27095                 html: 'Tidy HTML Source',
27096                 handler: function(a,b) {
27097                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27098                     editorcore.syncValue();
27099                 },
27100                 tabIndex:-1
27101             });
27102             
27103             
27104             tb.add(cmenu);
27105         }
27106          
27107         if (!this.disable.specialElements) {
27108             var semenu = {
27109                 text: "Other;",
27110                 cls: 'x-edit-none',
27111                 menu : {
27112                     items : []
27113                 }
27114             };
27115             for (var i =0; i < this.specialElements.length; i++) {
27116                 semenu.menu.items.push(
27117                     Roo.apply({ 
27118                         handler: function(a,b) {
27119                             editor.insertAtCursor(this.ihtml);
27120                         }
27121                     }, this.specialElements[i])
27122                 );
27123                     
27124             }
27125             
27126             tb.add(semenu);
27127             
27128             
27129         }
27130          
27131         
27132         if (this.btns) {
27133             for(var i =0; i< this.btns.length;i++) {
27134                 var b = Roo.factory(this.btns[i],Roo.form);
27135                 b.cls =  'x-edit-none';
27136                 b.scope = editorcore;
27137                 tb.add(b);
27138             }
27139         
27140         }
27141         
27142         
27143         
27144         // disable everything...
27145         
27146         this.tb.items.each(function(item){
27147            if(item.id != editorcore.frameId+ '-sourceedit'){
27148                 item.disable();
27149             }
27150         });
27151         this.rendered = true;
27152         
27153         // the all the btns;
27154         editor.on('editorevent', this.updateToolbar, this);
27155         // other toolbars need to implement this..
27156         //editor.on('editmodechange', this.updateToolbar, this);
27157     },
27158     
27159     
27160     relayBtnCmd : function(btn) {
27161         this.editorcore.relayCmd(btn.cmd);
27162     },
27163     // private used internally
27164     createLink : function(){
27165         Roo.log("create link?");
27166         var url = prompt(this.createLinkText, this.defaultLinkValue);
27167         if(url && url != 'http:/'+'/'){
27168             this.editorcore.relayCmd('createlink', url);
27169         }
27170     },
27171
27172     
27173     /**
27174      * Protected method that will not generally be called directly. It triggers
27175      * a toolbar update by reading the markup state of the current selection in the editor.
27176      */
27177     updateToolbar: function(){
27178
27179         if(!this.editorcore.activated){
27180             this.editor.onFirstFocus();
27181             return;
27182         }
27183
27184         var btns = this.tb.items.map, 
27185             doc = this.editorcore.doc,
27186             frameId = this.editorcore.frameId;
27187
27188         if(!this.disable.font && !Roo.isSafari){
27189             /*
27190             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27191             if(name != this.fontSelect.dom.value){
27192                 this.fontSelect.dom.value = name;
27193             }
27194             */
27195         }
27196         if(!this.disable.format){
27197             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27198             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27199             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27200         }
27201         if(!this.disable.alignments){
27202             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27203             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27204             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27205         }
27206         if(!Roo.isSafari && !this.disable.lists){
27207             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27208             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27209         }
27210         
27211         var ans = this.editorcore.getAllAncestors();
27212         if (this.formatCombo) {
27213             
27214             
27215             var store = this.formatCombo.store;
27216             this.formatCombo.setValue("");
27217             for (var i =0; i < ans.length;i++) {
27218                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27219                     // select it..
27220                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27221                     break;
27222                 }
27223             }
27224         }
27225         
27226         
27227         
27228         // hides menus... - so this cant be on a menu...
27229         Roo.menu.MenuMgr.hideAll();
27230
27231         //this.editorsyncValue();
27232     },
27233    
27234     
27235     createFontOptions : function(){
27236         var buf = [], fs = this.fontFamilies, ff, lc;
27237         
27238         
27239         
27240         for(var i = 0, len = fs.length; i< len; i++){
27241             ff = fs[i];
27242             lc = ff.toLowerCase();
27243             buf.push(
27244                 '<option value="',lc,'" style="font-family:',ff,';"',
27245                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27246                     ff,
27247                 '</option>'
27248             );
27249         }
27250         return buf.join('');
27251     },
27252     
27253     toggleSourceEdit : function(sourceEditMode){
27254         
27255         Roo.log("toolbar toogle");
27256         if(sourceEditMode === undefined){
27257             sourceEditMode = !this.sourceEditMode;
27258         }
27259         this.sourceEditMode = sourceEditMode === true;
27260         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27261         // just toggle the button?
27262         if(btn.pressed !== this.sourceEditMode){
27263             btn.toggle(this.sourceEditMode);
27264             return;
27265         }
27266         
27267         if(sourceEditMode){
27268             Roo.log("disabling buttons");
27269             this.tb.items.each(function(item){
27270                 if(item.cmd != 'sourceedit'){
27271                     item.disable();
27272                 }
27273             });
27274           
27275         }else{
27276             Roo.log("enabling buttons");
27277             if(this.editorcore.initialized){
27278                 this.tb.items.each(function(item){
27279                     item.enable();
27280                 });
27281             }
27282             
27283         }
27284         Roo.log("calling toggole on editor");
27285         // tell the editor that it's been pressed..
27286         this.editor.toggleSourceEdit(sourceEditMode);
27287        
27288     },
27289      /**
27290      * Object collection of toolbar tooltips for the buttons in the editor. The key
27291      * is the command id associated with that button and the value is a valid QuickTips object.
27292      * For example:
27293 <pre><code>
27294 {
27295     bold : {
27296         title: 'Bold (Ctrl+B)',
27297         text: 'Make the selected text bold.',
27298         cls: 'x-html-editor-tip'
27299     },
27300     italic : {
27301         title: 'Italic (Ctrl+I)',
27302         text: 'Make the selected text italic.',
27303         cls: 'x-html-editor-tip'
27304     },
27305     ...
27306 </code></pre>
27307     * @type Object
27308      */
27309     buttonTips : {
27310         bold : {
27311             title: 'Bold (Ctrl+B)',
27312             text: 'Make the selected text bold.',
27313             cls: 'x-html-editor-tip'
27314         },
27315         italic : {
27316             title: 'Italic (Ctrl+I)',
27317             text: 'Make the selected text italic.',
27318             cls: 'x-html-editor-tip'
27319         },
27320         underline : {
27321             title: 'Underline (Ctrl+U)',
27322             text: 'Underline the selected text.',
27323             cls: 'x-html-editor-tip'
27324         },
27325         increasefontsize : {
27326             title: 'Grow Text',
27327             text: 'Increase the font size.',
27328             cls: 'x-html-editor-tip'
27329         },
27330         decreasefontsize : {
27331             title: 'Shrink Text',
27332             text: 'Decrease the font size.',
27333             cls: 'x-html-editor-tip'
27334         },
27335         backcolor : {
27336             title: 'Text Highlight Color',
27337             text: 'Change the background color of the selected text.',
27338             cls: 'x-html-editor-tip'
27339         },
27340         forecolor : {
27341             title: 'Font Color',
27342             text: 'Change the color of the selected text.',
27343             cls: 'x-html-editor-tip'
27344         },
27345         justifyleft : {
27346             title: 'Align Text Left',
27347             text: 'Align text to the left.',
27348             cls: 'x-html-editor-tip'
27349         },
27350         justifycenter : {
27351             title: 'Center Text',
27352             text: 'Center text in the editor.',
27353             cls: 'x-html-editor-tip'
27354         },
27355         justifyright : {
27356             title: 'Align Text Right',
27357             text: 'Align text to the right.',
27358             cls: 'x-html-editor-tip'
27359         },
27360         insertunorderedlist : {
27361             title: 'Bullet List',
27362             text: 'Start a bulleted list.',
27363             cls: 'x-html-editor-tip'
27364         },
27365         insertorderedlist : {
27366             title: 'Numbered List',
27367             text: 'Start a numbered list.',
27368             cls: 'x-html-editor-tip'
27369         },
27370         createlink : {
27371             title: 'Hyperlink',
27372             text: 'Make the selected text a hyperlink.',
27373             cls: 'x-html-editor-tip'
27374         },
27375         sourceedit : {
27376             title: 'Source Edit',
27377             text: 'Switch to source editing mode.',
27378             cls: 'x-html-editor-tip'
27379         }
27380     },
27381     // private
27382     onDestroy : function(){
27383         if(this.rendered){
27384             
27385             this.tb.items.each(function(item){
27386                 if(item.menu){
27387                     item.menu.removeAll();
27388                     if(item.menu.el){
27389                         item.menu.el.destroy();
27390                     }
27391                 }
27392                 item.destroy();
27393             });
27394              
27395         }
27396     },
27397     onFirstFocus: function() {
27398         this.tb.items.each(function(item){
27399            item.enable();
27400         });
27401     }
27402 });
27403
27404
27405
27406
27407 // <script type="text/javascript">
27408 /*
27409  * Based on
27410  * Ext JS Library 1.1.1
27411  * Copyright(c) 2006-2007, Ext JS, LLC.
27412  *  
27413  
27414  */
27415
27416  
27417 /**
27418  * @class Roo.form.HtmlEditor.ToolbarContext
27419  * Context Toolbar
27420  * 
27421  * Usage:
27422  *
27423  new Roo.form.HtmlEditor({
27424     ....
27425     toolbars : [
27426         { xtype: 'ToolbarStandard', styles : {} }
27427         { xtype: 'ToolbarContext', disable : {} }
27428     ]
27429 })
27430
27431      
27432  * 
27433  * @config : {Object} disable List of elements to disable.. (not done yet.)
27434  * @config : {Object} styles  Map of styles available.
27435  * 
27436  */
27437
27438 Roo.form.HtmlEditor.ToolbarContext = function(config)
27439 {
27440     
27441     Roo.apply(this, config);
27442     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27443     // dont call parent... till later.
27444     this.styles = this.styles || {};
27445 }
27446
27447  
27448
27449 Roo.form.HtmlEditor.ToolbarContext.types = {
27450     'IMG' : {
27451         width : {
27452             title: "Width",
27453             width: 40
27454         },
27455         height:  {
27456             title: "Height",
27457             width: 40
27458         },
27459         align: {
27460             title: "Align",
27461             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27462             width : 80
27463             
27464         },
27465         border: {
27466             title: "Border",
27467             width: 40
27468         },
27469         alt: {
27470             title: "Alt",
27471             width: 120
27472         },
27473         src : {
27474             title: "Src",
27475             width: 220
27476         }
27477         
27478     },
27479     'A' : {
27480         name : {
27481             title: "Name",
27482             width: 50
27483         },
27484         target:  {
27485             title: "Target",
27486             width: 120
27487         },
27488         href:  {
27489             title: "Href",
27490             width: 220
27491         } // border?
27492         
27493     },
27494     'TABLE' : {
27495         rows : {
27496             title: "Rows",
27497             width: 20
27498         },
27499         cols : {
27500             title: "Cols",
27501             width: 20
27502         },
27503         width : {
27504             title: "Width",
27505             width: 40
27506         },
27507         height : {
27508             title: "Height",
27509             width: 40
27510         },
27511         border : {
27512             title: "Border",
27513             width: 20
27514         }
27515     },
27516     'TD' : {
27517         width : {
27518             title: "Width",
27519             width: 40
27520         },
27521         height : {
27522             title: "Height",
27523             width: 40
27524         },   
27525         align: {
27526             title: "Align",
27527             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27528             width: 80
27529         },
27530         valign: {
27531             title: "Valign",
27532             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27533             width: 80
27534         },
27535         colspan: {
27536             title: "Colspan",
27537             width: 20
27538             
27539         },
27540          'font-family'  : {
27541             title : "Font",
27542             style : 'fontFamily',
27543             displayField: 'display',
27544             optname : 'font-family',
27545             width: 140
27546         }
27547     },
27548     'INPUT' : {
27549         name : {
27550             title: "name",
27551             width: 120
27552         },
27553         value : {
27554             title: "Value",
27555             width: 120
27556         },
27557         width : {
27558             title: "Width",
27559             width: 40
27560         }
27561     },
27562     'LABEL' : {
27563         'for' : {
27564             title: "For",
27565             width: 120
27566         }
27567     },
27568     'TEXTAREA' : {
27569           name : {
27570             title: "name",
27571             width: 120
27572         },
27573         rows : {
27574             title: "Rows",
27575             width: 20
27576         },
27577         cols : {
27578             title: "Cols",
27579             width: 20
27580         }
27581     },
27582     'SELECT' : {
27583         name : {
27584             title: "name",
27585             width: 120
27586         },
27587         selectoptions : {
27588             title: "Options",
27589             width: 200
27590         }
27591     },
27592     
27593     // should we really allow this??
27594     // should this just be 
27595     'BODY' : {
27596         title : {
27597             title: "Title",
27598             width: 200,
27599             disabled : true
27600         }
27601     },
27602     'SPAN' : {
27603         'font-family'  : {
27604             title : "Font",
27605             style : 'fontFamily',
27606             displayField: 'display',
27607             optname : 'font-family',
27608             width: 140
27609         }
27610     },
27611     'DIV' : {
27612         'font-family'  : {
27613             title : "Font",
27614             style : 'fontFamily',
27615             displayField: 'display',
27616             optname : 'font-family',
27617             width: 140
27618         }
27619     },
27620      'P' : {
27621         'font-family'  : {
27622             title : "Font",
27623             style : 'fontFamily',
27624             displayField: 'display',
27625             optname : 'font-family',
27626             width: 140
27627         }
27628     },
27629     
27630     '*' : {
27631         // empty..
27632     }
27633
27634 };
27635
27636 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27637 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27638
27639 Roo.form.HtmlEditor.ToolbarContext.options = {
27640         'font-family'  : [ 
27641                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27642                 [ 'Courier New', 'Courier New'],
27643                 [ 'Tahoma', 'Tahoma'],
27644                 [ 'Times New Roman,serif', 'Times'],
27645                 [ 'Verdana','Verdana' ]
27646         ]
27647 };
27648
27649 // fixme - these need to be configurable..
27650  
27651
27652 Roo.form.HtmlEditor.ToolbarContext.types
27653
27654
27655 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27656     
27657     tb: false,
27658     
27659     rendered: false,
27660     
27661     editor : false,
27662     editorcore : false,
27663     /**
27664      * @cfg {Object} disable  List of toolbar elements to disable
27665          
27666      */
27667     disable : false,
27668     /**
27669      * @cfg {Object} styles List of styles 
27670      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27671      *
27672      * These must be defined in the page, so they get rendered correctly..
27673      * .headline { }
27674      * TD.underline { }
27675      * 
27676      */
27677     styles : false,
27678     
27679     options: false,
27680     
27681     toolbars : false,
27682     
27683     init : function(editor)
27684     {
27685         this.editor = editor;
27686         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27687         var editorcore = this.editorcore;
27688         
27689         var fid = editorcore.frameId;
27690         var etb = this;
27691         function btn(id, toggle, handler){
27692             var xid = fid + '-'+ id ;
27693             return {
27694                 id : xid,
27695                 cmd : id,
27696                 cls : 'x-btn-icon x-edit-'+id,
27697                 enableToggle:toggle !== false,
27698                 scope: editorcore, // was editor...
27699                 handler:handler||editorcore.relayBtnCmd,
27700                 clickEvent:'mousedown',
27701                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27702                 tabIndex:-1
27703             };
27704         }
27705         // create a new element.
27706         var wdiv = editor.wrap.createChild({
27707                 tag: 'div'
27708             }, editor.wrap.dom.firstChild.nextSibling, true);
27709         
27710         // can we do this more than once??
27711         
27712          // stop form submits
27713       
27714  
27715         // disable everything...
27716         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27717         this.toolbars = {};
27718            
27719         for (var i in  ty) {
27720           
27721             this.toolbars[i] = this.buildToolbar(ty[i],i);
27722         }
27723         this.tb = this.toolbars.BODY;
27724         this.tb.el.show();
27725         this.buildFooter();
27726         this.footer.show();
27727         editor.on('hide', function( ) { this.footer.hide() }, this);
27728         editor.on('show', function( ) { this.footer.show() }, this);
27729         
27730          
27731         this.rendered = true;
27732         
27733         // the all the btns;
27734         editor.on('editorevent', this.updateToolbar, this);
27735         // other toolbars need to implement this..
27736         //editor.on('editmodechange', this.updateToolbar, this);
27737     },
27738     
27739     
27740     
27741     /**
27742      * Protected method that will not generally be called directly. It triggers
27743      * a toolbar update by reading the markup state of the current selection in the editor.
27744      */
27745     updateToolbar: function(editor,ev,sel){
27746
27747         //Roo.log(ev);
27748         // capture mouse up - this is handy for selecting images..
27749         // perhaps should go somewhere else...
27750         if(!this.editorcore.activated){
27751              this.editor.onFirstFocus();
27752             return;
27753         }
27754         
27755         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27756         // selectNode - might want to handle IE?
27757         if (ev &&
27758             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27759             ev.target && ev.target.tagName == 'IMG') {
27760             // they have click on an image...
27761             // let's see if we can change the selection...
27762             sel = ev.target;
27763          
27764               var nodeRange = sel.ownerDocument.createRange();
27765             try {
27766                 nodeRange.selectNode(sel);
27767             } catch (e) {
27768                 nodeRange.selectNodeContents(sel);
27769             }
27770             //nodeRange.collapse(true);
27771             var s = this.editorcore.win.getSelection();
27772             s.removeAllRanges();
27773             s.addRange(nodeRange);
27774         }  
27775         
27776       
27777         var updateFooter = sel ? false : true;
27778         
27779         
27780         var ans = this.editorcore.getAllAncestors();
27781         
27782         // pick
27783         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27784         
27785         if (!sel) { 
27786             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
27787             sel = sel ? sel : this.editorcore.doc.body;
27788             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
27789             
27790         }
27791         // pick a menu that exists..
27792         var tn = sel.tagName.toUpperCase();
27793         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27794         
27795         tn = sel.tagName.toUpperCase();
27796         
27797         var lastSel = this.tb.selectedNode
27798         
27799         this.tb.selectedNode = sel;
27800         
27801         // if current menu does not match..
27802         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27803                 
27804             this.tb.el.hide();
27805             ///console.log("show: " + tn);
27806             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27807             this.tb.el.show();
27808             // update name
27809             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27810             
27811             
27812             // update attributes
27813             if (this.tb.fields) {
27814                 this.tb.fields.each(function(e) {
27815                     if (e.stylename) {
27816                         e.setValue(sel.style[e.stylename]);
27817                         return;
27818                     } 
27819                    e.setValue(sel.getAttribute(e.attrname));
27820                 });
27821             }
27822             
27823             var hasStyles = false;
27824             for(var i in this.styles) {
27825                 hasStyles = true;
27826                 break;
27827             }
27828             
27829             // update styles
27830             if (hasStyles) { 
27831                 var st = this.tb.fields.item(0);
27832                 
27833                 st.store.removeAll();
27834                
27835                 
27836                 var cn = sel.className.split(/\s+/);
27837                 
27838                 var avs = [];
27839                 if (this.styles['*']) {
27840                     
27841                     Roo.each(this.styles['*'], function(v) {
27842                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27843                     });
27844                 }
27845                 if (this.styles[tn]) { 
27846                     Roo.each(this.styles[tn], function(v) {
27847                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27848                     });
27849                 }
27850                 
27851                 st.store.loadData(avs);
27852                 st.collapse();
27853                 st.setValue(cn);
27854             }
27855             // flag our selected Node.
27856             this.tb.selectedNode = sel;
27857            
27858            
27859             Roo.menu.MenuMgr.hideAll();
27860
27861         }
27862         
27863         if (!updateFooter) {
27864             //this.footDisp.dom.innerHTML = ''; 
27865             return;
27866         }
27867         // update the footer
27868         //
27869         var html = '';
27870         
27871         this.footerEls = ans.reverse();
27872         Roo.each(this.footerEls, function(a,i) {
27873             if (!a) { return; }
27874             html += html.length ? ' &gt; '  :  '';
27875             
27876             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27877             
27878         });
27879        
27880         // 
27881         var sz = this.footDisp.up('td').getSize();
27882         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27883         this.footDisp.dom.style.marginLeft = '5px';
27884         
27885         this.footDisp.dom.style.overflow = 'hidden';
27886         
27887         this.footDisp.dom.innerHTML = html;
27888             
27889         //this.editorsyncValue();
27890     },
27891      
27892     
27893    
27894        
27895     // private
27896     onDestroy : function(){
27897         if(this.rendered){
27898             
27899             this.tb.items.each(function(item){
27900                 if(item.menu){
27901                     item.menu.removeAll();
27902                     if(item.menu.el){
27903                         item.menu.el.destroy();
27904                     }
27905                 }
27906                 item.destroy();
27907             });
27908              
27909         }
27910     },
27911     onFirstFocus: function() {
27912         // need to do this for all the toolbars..
27913         this.tb.items.each(function(item){
27914            item.enable();
27915         });
27916     },
27917     buildToolbar: function(tlist, nm)
27918     {
27919         var editor = this.editor;
27920         var editorcore = this.editorcore;
27921          // create a new element.
27922         var wdiv = editor.wrap.createChild({
27923                 tag: 'div'
27924             }, editor.wrap.dom.firstChild.nextSibling, true);
27925         
27926        
27927         var tb = new Roo.Toolbar(wdiv);
27928         // add the name..
27929         
27930         tb.add(nm+ ":&nbsp;");
27931         
27932         var styles = [];
27933         for(var i in this.styles) {
27934             styles.push(i);
27935         }
27936         
27937         // styles...
27938         if (styles && styles.length) {
27939             
27940             // this needs a multi-select checkbox...
27941             tb.addField( new Roo.form.ComboBox({
27942                 store: new Roo.data.SimpleStore({
27943                     id : 'val',
27944                     fields: ['val', 'selected'],
27945                     data : [] 
27946                 }),
27947                 name : '-roo-edit-className',
27948                 attrname : 'className',
27949                 displayField: 'val',
27950                 typeAhead: false,
27951                 mode: 'local',
27952                 editable : false,
27953                 triggerAction: 'all',
27954                 emptyText:'Select Style',
27955                 selectOnFocus:true,
27956                 width: 130,
27957                 listeners : {
27958                     'select': function(c, r, i) {
27959                         // initial support only for on class per el..
27960                         tb.selectedNode.className =  r ? r.get('val') : '';
27961                         editorcore.syncValue();
27962                     }
27963                 }
27964     
27965             }));
27966         }
27967         
27968         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27969         var tbops = tbc.options;
27970         
27971         for (var i in tlist) {
27972             
27973             var item = tlist[i];
27974             tb.add(item.title + ":&nbsp;");
27975             
27976             
27977             //optname == used so you can configure the options available..
27978             var opts = item.opts ? item.opts : false;
27979             if (item.optname) {
27980                 opts = tbops[item.optname];
27981            
27982             }
27983             
27984             if (opts) {
27985                 // opts == pulldown..
27986                 tb.addField( new Roo.form.ComboBox({
27987                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27988                         id : 'val',
27989                         fields: ['val', 'display'],
27990                         data : opts  
27991                     }),
27992                     name : '-roo-edit-' + i,
27993                     attrname : i,
27994                     stylename : item.style ? item.style : false,
27995                     displayField: item.displayField ? item.displayField : 'val',
27996                     valueField :  'val',
27997                     typeAhead: false,
27998                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27999                     editable : false,
28000                     triggerAction: 'all',
28001                     emptyText:'Select',
28002                     selectOnFocus:true,
28003                     width: item.width ? item.width  : 130,
28004                     listeners : {
28005                         'select': function(c, r, i) {
28006                             if (c.stylename) {
28007                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28008                                 return;
28009                             }
28010                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28011                         }
28012                     }
28013
28014                 }));
28015                 continue;
28016                     
28017                  
28018                 
28019                 tb.addField( new Roo.form.TextField({
28020                     name: i,
28021                     width: 100,
28022                     //allowBlank:false,
28023                     value: ''
28024                 }));
28025                 continue;
28026             }
28027             tb.addField( new Roo.form.TextField({
28028                 name: '-roo-edit-' + i,
28029                 attrname : i,
28030                 
28031                 width: item.width,
28032                 //allowBlank:true,
28033                 value: '',
28034                 listeners: {
28035                     'change' : function(f, nv, ov) {
28036                         tb.selectedNode.setAttribute(f.attrname, nv);
28037                     }
28038                 }
28039             }));
28040              
28041         }
28042         tb.addFill();
28043         var _this = this;
28044         tb.addButton( {
28045             text: 'Remove Tag',
28046     
28047             listeners : {
28048                 click : function ()
28049                 {
28050                     // remove
28051                     // undo does not work.
28052                      
28053                     var sn = tb.selectedNode;
28054                     
28055                     var pn = sn.parentNode;
28056                     
28057                     var stn =  sn.childNodes[0];
28058                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28059                     while (sn.childNodes.length) {
28060                         var node = sn.childNodes[0];
28061                         sn.removeChild(node);
28062                         //Roo.log(node);
28063                         pn.insertBefore(node, sn);
28064                         
28065                     }
28066                     pn.removeChild(sn);
28067                     var range = editorcore.createRange();
28068         
28069                     range.setStart(stn,0);
28070                     range.setEnd(en,0); //????
28071                     //range.selectNode(sel);
28072                     
28073                     
28074                     var selection = editorcore.getSelection();
28075                     selection.removeAllRanges();
28076                     selection.addRange(range);
28077                     
28078                     
28079                     
28080                     //_this.updateToolbar(null, null, pn);
28081                     _this.updateToolbar(null, null, null);
28082                     _this.footDisp.dom.innerHTML = ''; 
28083                 }
28084             }
28085             
28086                     
28087                 
28088             
28089         });
28090         
28091         
28092         tb.el.on('click', function(e){
28093             e.preventDefault(); // what does this do?
28094         });
28095         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28096         tb.el.hide();
28097         tb.name = nm;
28098         // dont need to disable them... as they will get hidden
28099         return tb;
28100          
28101         
28102     },
28103     buildFooter : function()
28104     {
28105         
28106         var fel = this.editor.wrap.createChild();
28107         this.footer = new Roo.Toolbar(fel);
28108         // toolbar has scrolly on left / right?
28109         var footDisp= new Roo.Toolbar.Fill();
28110         var _t = this;
28111         this.footer.add(
28112             {
28113                 text : '&lt;',
28114                 xtype: 'Button',
28115                 handler : function() {
28116                     _t.footDisp.scrollTo('left',0,true)
28117                 }
28118             }
28119         );
28120         this.footer.add( footDisp );
28121         this.footer.add( 
28122             {
28123                 text : '&gt;',
28124                 xtype: 'Button',
28125                 handler : function() {
28126                     // no animation..
28127                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28128                 }
28129             }
28130         );
28131         var fel = Roo.get(footDisp.el);
28132         fel.addClass('x-editor-context');
28133         this.footDispWrap = fel; 
28134         this.footDispWrap.overflow  = 'hidden';
28135         
28136         this.footDisp = fel.createChild();
28137         this.footDispWrap.on('click', this.onContextClick, this)
28138         
28139         
28140     },
28141     onContextClick : function (ev,dom)
28142     {
28143         ev.preventDefault();
28144         var  cn = dom.className;
28145         //Roo.log(cn);
28146         if (!cn.match(/x-ed-loc-/)) {
28147             return;
28148         }
28149         var n = cn.split('-').pop();
28150         var ans = this.footerEls;
28151         var sel = ans[n];
28152         
28153          // pick
28154         var range = this.editorcore.createRange();
28155         
28156         range.selectNodeContents(sel);
28157         //range.selectNode(sel);
28158         
28159         
28160         var selection = this.editorcore.getSelection();
28161         selection.removeAllRanges();
28162         selection.addRange(range);
28163         
28164         
28165         
28166         this.updateToolbar(null, null, sel);
28167         
28168         
28169     }
28170     
28171     
28172     
28173     
28174     
28175 });
28176
28177
28178
28179
28180
28181 /*
28182  * Based on:
28183  * Ext JS Library 1.1.1
28184  * Copyright(c) 2006-2007, Ext JS, LLC.
28185  *
28186  * Originally Released Under LGPL - original licence link has changed is not relivant.
28187  *
28188  * Fork - LGPL
28189  * <script type="text/javascript">
28190  */
28191  
28192 /**
28193  * @class Roo.form.BasicForm
28194  * @extends Roo.util.Observable
28195  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28196  * @constructor
28197  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28198  * @param {Object} config Configuration options
28199  */
28200 Roo.form.BasicForm = function(el, config){
28201     this.allItems = [];
28202     this.childForms = [];
28203     Roo.apply(this, config);
28204     /*
28205      * The Roo.form.Field items in this form.
28206      * @type MixedCollection
28207      */
28208      
28209      
28210     this.items = new Roo.util.MixedCollection(false, function(o){
28211         return o.id || (o.id = Roo.id());
28212     });
28213     this.addEvents({
28214         /**
28215          * @event beforeaction
28216          * Fires before any action is performed. Return false to cancel the action.
28217          * @param {Form} this
28218          * @param {Action} action The action to be performed
28219          */
28220         beforeaction: true,
28221         /**
28222          * @event actionfailed
28223          * Fires when an action fails.
28224          * @param {Form} this
28225          * @param {Action} action The action that failed
28226          */
28227         actionfailed : true,
28228         /**
28229          * @event actioncomplete
28230          * Fires when an action is completed.
28231          * @param {Form} this
28232          * @param {Action} action The action that completed
28233          */
28234         actioncomplete : true
28235     });
28236     if(el){
28237         this.initEl(el);
28238     }
28239     Roo.form.BasicForm.superclass.constructor.call(this);
28240 };
28241
28242 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28243     /**
28244      * @cfg {String} method
28245      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28246      */
28247     /**
28248      * @cfg {DataReader} reader
28249      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28250      * This is optional as there is built-in support for processing JSON.
28251      */
28252     /**
28253      * @cfg {DataReader} errorReader
28254      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28255      * This is completely optional as there is built-in support for processing JSON.
28256      */
28257     /**
28258      * @cfg {String} url
28259      * The URL to use for form actions if one isn't supplied in the action options.
28260      */
28261     /**
28262      * @cfg {Boolean} fileUpload
28263      * Set to true if this form is a file upload.
28264      */
28265      
28266     /**
28267      * @cfg {Object} baseParams
28268      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28269      */
28270      /**
28271      
28272     /**
28273      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28274      */
28275     timeout: 30,
28276
28277     // private
28278     activeAction : null,
28279
28280     /**
28281      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28282      * or setValues() data instead of when the form was first created.
28283      */
28284     trackResetOnLoad : false,
28285     
28286     
28287     /**
28288      * childForms - used for multi-tab forms
28289      * @type {Array}
28290      */
28291     childForms : false,
28292     
28293     /**
28294      * allItems - full list of fields.
28295      * @type {Array}
28296      */
28297     allItems : false,
28298     
28299     /**
28300      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28301      * element by passing it or its id or mask the form itself by passing in true.
28302      * @type Mixed
28303      */
28304     waitMsgTarget : false,
28305
28306     // private
28307     initEl : function(el){
28308         this.el = Roo.get(el);
28309         this.id = this.el.id || Roo.id();
28310         this.el.on('submit', this.onSubmit, this);
28311         this.el.addClass('x-form');
28312     },
28313
28314     // private
28315     onSubmit : function(e){
28316         e.stopEvent();
28317     },
28318
28319     /**
28320      * Returns true if client-side validation on the form is successful.
28321      * @return Boolean
28322      */
28323     isValid : function(){
28324         var valid = true;
28325         this.items.each(function(f){
28326            if(!f.validate()){
28327                valid = false;
28328            }
28329         });
28330         return valid;
28331     },
28332
28333     /**
28334      * Returns true if any fields in this form have changed since their original load.
28335      * @return Boolean
28336      */
28337     isDirty : function(){
28338         var dirty = false;
28339         this.items.each(function(f){
28340            if(f.isDirty()){
28341                dirty = true;
28342                return false;
28343            }
28344         });
28345         return dirty;
28346     },
28347
28348     /**
28349      * Performs a predefined action (submit or load) or custom actions you define on this form.
28350      * @param {String} actionName The name of the action type
28351      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28352      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28353      * accept other config options):
28354      * <pre>
28355 Property          Type             Description
28356 ----------------  ---------------  ----------------------------------------------------------------------------------
28357 url               String           The url for the action (defaults to the form's url)
28358 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28359 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28360 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28361                                    validate the form on the client (defaults to false)
28362      * </pre>
28363      * @return {BasicForm} this
28364      */
28365     doAction : function(action, options){
28366         if(typeof action == 'string'){
28367             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28368         }
28369         if(this.fireEvent('beforeaction', this, action) !== false){
28370             this.beforeAction(action);
28371             action.run.defer(100, action);
28372         }
28373         return this;
28374     },
28375
28376     /**
28377      * Shortcut to do a submit action.
28378      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28379      * @return {BasicForm} this
28380      */
28381     submit : function(options){
28382         this.doAction('submit', options);
28383         return this;
28384     },
28385
28386     /**
28387      * Shortcut to do a load action.
28388      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28389      * @return {BasicForm} this
28390      */
28391     load : function(options){
28392         this.doAction('load', options);
28393         return this;
28394     },
28395
28396     /**
28397      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28398      * @param {Record} record The record to edit
28399      * @return {BasicForm} this
28400      */
28401     updateRecord : function(record){
28402         record.beginEdit();
28403         var fs = record.fields;
28404         fs.each(function(f){
28405             var field = this.findField(f.name);
28406             if(field){
28407                 record.set(f.name, field.getValue());
28408             }
28409         }, this);
28410         record.endEdit();
28411         return this;
28412     },
28413
28414     /**
28415      * Loads an Roo.data.Record into this form.
28416      * @param {Record} record The record to load
28417      * @return {BasicForm} this
28418      */
28419     loadRecord : function(record){
28420         this.setValues(record.data);
28421         return this;
28422     },
28423
28424     // private
28425     beforeAction : function(action){
28426         var o = action.options;
28427         
28428        
28429         if(this.waitMsgTarget === true){
28430             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28431         }else if(this.waitMsgTarget){
28432             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28433             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28434         }else {
28435             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28436         }
28437          
28438     },
28439
28440     // private
28441     afterAction : function(action, success){
28442         this.activeAction = null;
28443         var o = action.options;
28444         
28445         if(this.waitMsgTarget === true){
28446             this.el.unmask();
28447         }else if(this.waitMsgTarget){
28448             this.waitMsgTarget.unmask();
28449         }else{
28450             Roo.MessageBox.updateProgress(1);
28451             Roo.MessageBox.hide();
28452         }
28453          
28454         if(success){
28455             if(o.reset){
28456                 this.reset();
28457             }
28458             Roo.callback(o.success, o.scope, [this, action]);
28459             this.fireEvent('actioncomplete', this, action);
28460             
28461         }else{
28462             
28463             // failure condition..
28464             // we have a scenario where updates need confirming.
28465             // eg. if a locking scenario exists..
28466             // we look for { errors : { needs_confirm : true }} in the response.
28467             if (
28468                 (typeof(action.result) != 'undefined')  &&
28469                 (typeof(action.result.errors) != 'undefined')  &&
28470                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28471            ){
28472                 var _t = this;
28473                 Roo.MessageBox.confirm(
28474                     "Change requires confirmation",
28475                     action.result.errorMsg,
28476                     function(r) {
28477                         if (r != 'yes') {
28478                             return;
28479                         }
28480                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28481                     }
28482                     
28483                 );
28484                 
28485                 
28486                 
28487                 return;
28488             }
28489             
28490             Roo.callback(o.failure, o.scope, [this, action]);
28491             // show an error message if no failed handler is set..
28492             if (!this.hasListener('actionfailed')) {
28493                 Roo.MessageBox.alert("Error",
28494                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28495                         action.result.errorMsg :
28496                         "Saving Failed, please check your entries or try again"
28497                 );
28498             }
28499             
28500             this.fireEvent('actionfailed', this, action);
28501         }
28502         
28503     },
28504
28505     /**
28506      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28507      * @param {String} id The value to search for
28508      * @return Field
28509      */
28510     findField : function(id){
28511         var field = this.items.get(id);
28512         if(!field){
28513             this.items.each(function(f){
28514                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28515                     field = f;
28516                     return false;
28517                 }
28518             });
28519         }
28520         return field || null;
28521     },
28522
28523     /**
28524      * Add a secondary form to this one, 
28525      * Used to provide tabbed forms. One form is primary, with hidden values 
28526      * which mirror the elements from the other forms.
28527      * 
28528      * @param {Roo.form.Form} form to add.
28529      * 
28530      */
28531     addForm : function(form)
28532     {
28533        
28534         if (this.childForms.indexOf(form) > -1) {
28535             // already added..
28536             return;
28537         }
28538         this.childForms.push(form);
28539         var n = '';
28540         Roo.each(form.allItems, function (fe) {
28541             
28542             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28543             if (this.findField(n)) { // already added..
28544                 return;
28545             }
28546             var add = new Roo.form.Hidden({
28547                 name : n
28548             });
28549             add.render(this.el);
28550             
28551             this.add( add );
28552         }, this);
28553         
28554     },
28555     /**
28556      * Mark fields in this form invalid in bulk.
28557      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28558      * @return {BasicForm} this
28559      */
28560     markInvalid : function(errors){
28561         if(errors instanceof Array){
28562             for(var i = 0, len = errors.length; i < len; i++){
28563                 var fieldError = errors[i];
28564                 var f = this.findField(fieldError.id);
28565                 if(f){
28566                     f.markInvalid(fieldError.msg);
28567                 }
28568             }
28569         }else{
28570             var field, id;
28571             for(id in errors){
28572                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28573                     field.markInvalid(errors[id]);
28574                 }
28575             }
28576         }
28577         Roo.each(this.childForms || [], function (f) {
28578             f.markInvalid(errors);
28579         });
28580         
28581         return this;
28582     },
28583
28584     /**
28585      * Set values for fields in this form in bulk.
28586      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28587      * @return {BasicForm} this
28588      */
28589     setValues : function(values){
28590         if(values instanceof Array){ // array of objects
28591             for(var i = 0, len = values.length; i < len; i++){
28592                 var v = values[i];
28593                 var f = this.findField(v.id);
28594                 if(f){
28595                     f.setValue(v.value);
28596                     if(this.trackResetOnLoad){
28597                         f.originalValue = f.getValue();
28598                     }
28599                 }
28600             }
28601         }else{ // object hash
28602             var field, id;
28603             for(id in values){
28604                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28605                     
28606                     if (field.setFromData && 
28607                         field.valueField && 
28608                         field.displayField &&
28609                         // combos' with local stores can 
28610                         // be queried via setValue()
28611                         // to set their value..
28612                         (field.store && !field.store.isLocal)
28613                         ) {
28614                         // it's a combo
28615                         var sd = { };
28616                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28617                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28618                         field.setFromData(sd);
28619                         
28620                     } else {
28621                         field.setValue(values[id]);
28622                     }
28623                     
28624                     
28625                     if(this.trackResetOnLoad){
28626                         field.originalValue = field.getValue();
28627                     }
28628                 }
28629             }
28630         }
28631          
28632         Roo.each(this.childForms || [], function (f) {
28633             f.setValues(values);
28634         });
28635                 
28636         return this;
28637     },
28638
28639     /**
28640      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28641      * they are returned as an array.
28642      * @param {Boolean} asString
28643      * @return {Object}
28644      */
28645     getValues : function(asString){
28646         if (this.childForms) {
28647             // copy values from the child forms
28648             Roo.each(this.childForms, function (f) {
28649                 this.setValues(f.getValues());
28650             }, this);
28651         }
28652         
28653         
28654         
28655         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28656         if(asString === true){
28657             return fs;
28658         }
28659         return Roo.urlDecode(fs);
28660     },
28661     
28662     /**
28663      * Returns the fields in this form as an object with key/value pairs. 
28664      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28665      * @return {Object}
28666      */
28667     getFieldValues : function(with_hidden)
28668     {
28669         if (this.childForms) {
28670             // copy values from the child forms
28671             // should this call getFieldValues - probably not as we do not currently copy
28672             // hidden fields when we generate..
28673             Roo.each(this.childForms, function (f) {
28674                 this.setValues(f.getValues());
28675             }, this);
28676         }
28677         
28678         var ret = {};
28679         this.items.each(function(f){
28680             if (!f.getName()) {
28681                 return;
28682             }
28683             var v = f.getValue();
28684             if (f.inputType =='radio') {
28685                 if (typeof(ret[f.getName()]) == 'undefined') {
28686                     ret[f.getName()] = ''; // empty..
28687                 }
28688                 
28689                 if (!f.el.dom.checked) {
28690                     return;
28691                     
28692                 }
28693                 v = f.el.dom.value;
28694                 
28695             }
28696             
28697             // not sure if this supported any more..
28698             if ((typeof(v) == 'object') && f.getRawValue) {
28699                 v = f.getRawValue() ; // dates..
28700             }
28701             // combo boxes where name != hiddenName...
28702             if (f.name != f.getName()) {
28703                 ret[f.name] = f.getRawValue();
28704             }
28705             ret[f.getName()] = v;
28706         });
28707         
28708         return ret;
28709     },
28710
28711     /**
28712      * Clears all invalid messages in this form.
28713      * @return {BasicForm} this
28714      */
28715     clearInvalid : function(){
28716         this.items.each(function(f){
28717            f.clearInvalid();
28718         });
28719         
28720         Roo.each(this.childForms || [], function (f) {
28721             f.clearInvalid();
28722         });
28723         
28724         
28725         return this;
28726     },
28727
28728     /**
28729      * Resets this form.
28730      * @return {BasicForm} this
28731      */
28732     reset : function(){
28733         this.items.each(function(f){
28734             f.reset();
28735         });
28736         
28737         Roo.each(this.childForms || [], function (f) {
28738             f.reset();
28739         });
28740        
28741         
28742         return this;
28743     },
28744
28745     /**
28746      * Add Roo.form components to this form.
28747      * @param {Field} field1
28748      * @param {Field} field2 (optional)
28749      * @param {Field} etc (optional)
28750      * @return {BasicForm} this
28751      */
28752     add : function(){
28753         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28754         return this;
28755     },
28756
28757
28758     /**
28759      * Removes a field from the items collection (does NOT remove its markup).
28760      * @param {Field} field
28761      * @return {BasicForm} this
28762      */
28763     remove : function(field){
28764         this.items.remove(field);
28765         return this;
28766     },
28767
28768     /**
28769      * Looks at the fields in this form, checks them for an id attribute,
28770      * and calls applyTo on the existing dom element with that id.
28771      * @return {BasicForm} this
28772      */
28773     render : function(){
28774         this.items.each(function(f){
28775             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28776                 f.applyTo(f.id);
28777             }
28778         });
28779         return this;
28780     },
28781
28782     /**
28783      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28784      * @param {Object} values
28785      * @return {BasicForm} this
28786      */
28787     applyToFields : function(o){
28788         this.items.each(function(f){
28789            Roo.apply(f, o);
28790         });
28791         return this;
28792     },
28793
28794     /**
28795      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28796      * @param {Object} values
28797      * @return {BasicForm} this
28798      */
28799     applyIfToFields : function(o){
28800         this.items.each(function(f){
28801            Roo.applyIf(f, o);
28802         });
28803         return this;
28804     }
28805 });
28806
28807 // back compat
28808 Roo.BasicForm = Roo.form.BasicForm;/*
28809  * Based on:
28810  * Ext JS Library 1.1.1
28811  * Copyright(c) 2006-2007, Ext JS, LLC.
28812  *
28813  * Originally Released Under LGPL - original licence link has changed is not relivant.
28814  *
28815  * Fork - LGPL
28816  * <script type="text/javascript">
28817  */
28818
28819 /**
28820  * @class Roo.form.Form
28821  * @extends Roo.form.BasicForm
28822  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28823  * @constructor
28824  * @param {Object} config Configuration options
28825  */
28826 Roo.form.Form = function(config){
28827     var xitems =  [];
28828     if (config.items) {
28829         xitems = config.items;
28830         delete config.items;
28831     }
28832    
28833     
28834     Roo.form.Form.superclass.constructor.call(this, null, config);
28835     this.url = this.url || this.action;
28836     if(!this.root){
28837         this.root = new Roo.form.Layout(Roo.applyIf({
28838             id: Roo.id()
28839         }, config));
28840     }
28841     this.active = this.root;
28842     /**
28843      * Array of all the buttons that have been added to this form via {@link addButton}
28844      * @type Array
28845      */
28846     this.buttons = [];
28847     this.allItems = [];
28848     this.addEvents({
28849         /**
28850          * @event clientvalidation
28851          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28852          * @param {Form} this
28853          * @param {Boolean} valid true if the form has passed client-side validation
28854          */
28855         clientvalidation: true,
28856         /**
28857          * @event rendered
28858          * Fires when the form is rendered
28859          * @param {Roo.form.Form} form
28860          */
28861         rendered : true
28862     });
28863     
28864     if (this.progressUrl) {
28865             // push a hidden field onto the list of fields..
28866             this.addxtype( {
28867                     xns: Roo.form, 
28868                     xtype : 'Hidden', 
28869                     name : 'UPLOAD_IDENTIFIER' 
28870             });
28871         }
28872         
28873     
28874     Roo.each(xitems, this.addxtype, this);
28875     
28876     
28877     
28878 };
28879
28880 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28881     /**
28882      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28883      */
28884     /**
28885      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28886      */
28887     /**
28888      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28889      */
28890     buttonAlign:'center',
28891
28892     /**
28893      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28894      */
28895     minButtonWidth:75,
28896
28897     /**
28898      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28899      * This property cascades to child containers if not set.
28900      */
28901     labelAlign:'left',
28902
28903     /**
28904      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28905      * fires a looping event with that state. This is required to bind buttons to the valid
28906      * state using the config value formBind:true on the button.
28907      */
28908     monitorValid : false,
28909
28910     /**
28911      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28912      */
28913     monitorPoll : 200,
28914     
28915     /**
28916      * @cfg {String} progressUrl - Url to return progress data 
28917      */
28918     
28919     progressUrl : false,
28920   
28921     /**
28922      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28923      * fields are added and the column is closed. If no fields are passed the column remains open
28924      * until end() is called.
28925      * @param {Object} config The config to pass to the column
28926      * @param {Field} field1 (optional)
28927      * @param {Field} field2 (optional)
28928      * @param {Field} etc (optional)
28929      * @return Column The column container object
28930      */
28931     column : function(c){
28932         var col = new Roo.form.Column(c);
28933         this.start(col);
28934         if(arguments.length > 1){ // duplicate code required because of Opera
28935             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28936             this.end();
28937         }
28938         return col;
28939     },
28940
28941     /**
28942      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28943      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28944      * until end() is called.
28945      * @param {Object} config The config to pass to the fieldset
28946      * @param {Field} field1 (optional)
28947      * @param {Field} field2 (optional)
28948      * @param {Field} etc (optional)
28949      * @return FieldSet The fieldset container object
28950      */
28951     fieldset : function(c){
28952         var fs = new Roo.form.FieldSet(c);
28953         this.start(fs);
28954         if(arguments.length > 1){ // duplicate code required because of Opera
28955             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28956             this.end();
28957         }
28958         return fs;
28959     },
28960
28961     /**
28962      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28963      * fields are added and the container is closed. If no fields are passed the container remains open
28964      * until end() is called.
28965      * @param {Object} config The config to pass to the Layout
28966      * @param {Field} field1 (optional)
28967      * @param {Field} field2 (optional)
28968      * @param {Field} etc (optional)
28969      * @return Layout The container object
28970      */
28971     container : function(c){
28972         var l = new Roo.form.Layout(c);
28973         this.start(l);
28974         if(arguments.length > 1){ // duplicate code required because of Opera
28975             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28976             this.end();
28977         }
28978         return l;
28979     },
28980
28981     /**
28982      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28983      * @param {Object} container A Roo.form.Layout or subclass of Layout
28984      * @return {Form} this
28985      */
28986     start : function(c){
28987         // cascade label info
28988         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28989         this.active.stack.push(c);
28990         c.ownerCt = this.active;
28991         this.active = c;
28992         return this;
28993     },
28994
28995     /**
28996      * Closes the current open container
28997      * @return {Form} this
28998      */
28999     end : function(){
29000         if(this.active == this.root){
29001             return this;
29002         }
29003         this.active = this.active.ownerCt;
29004         return this;
29005     },
29006
29007     /**
29008      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29009      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29010      * as the label of the field.
29011      * @param {Field} field1
29012      * @param {Field} field2 (optional)
29013      * @param {Field} etc. (optional)
29014      * @return {Form} this
29015      */
29016     add : function(){
29017         this.active.stack.push.apply(this.active.stack, arguments);
29018         this.allItems.push.apply(this.allItems,arguments);
29019         var r = [];
29020         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29021             if(a[i].isFormField){
29022                 r.push(a[i]);
29023             }
29024         }
29025         if(r.length > 0){
29026             Roo.form.Form.superclass.add.apply(this, r);
29027         }
29028         return this;
29029     },
29030     
29031
29032     
29033     
29034     
29035      /**
29036      * Find any element that has been added to a form, using it's ID or name
29037      * This can include framesets, columns etc. along with regular fields..
29038      * @param {String} id - id or name to find.
29039      
29040      * @return {Element} e - or false if nothing found.
29041      */
29042     findbyId : function(id)
29043     {
29044         var ret = false;
29045         if (!id) {
29046             return ret;
29047         }
29048         Roo.each(this.allItems, function(f){
29049             if (f.id == id || f.name == id ){
29050                 ret = f;
29051                 return false;
29052             }
29053         });
29054         return ret;
29055     },
29056
29057     
29058     
29059     /**
29060      * Render this form into the passed container. This should only be called once!
29061      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29062      * @return {Form} this
29063      */
29064     render : function(ct)
29065     {
29066         
29067         
29068         
29069         ct = Roo.get(ct);
29070         var o = this.autoCreate || {
29071             tag: 'form',
29072             method : this.method || 'POST',
29073             id : this.id || Roo.id()
29074         };
29075         this.initEl(ct.createChild(o));
29076
29077         this.root.render(this.el);
29078         
29079        
29080              
29081         this.items.each(function(f){
29082             f.render('x-form-el-'+f.id);
29083         });
29084
29085         if(this.buttons.length > 0){
29086             // tables are required to maintain order and for correct IE layout
29087             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29088                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29089                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29090             }}, null, true);
29091             var tr = tb.getElementsByTagName('tr')[0];
29092             for(var i = 0, len = this.buttons.length; i < len; i++) {
29093                 var b = this.buttons[i];
29094                 var td = document.createElement('td');
29095                 td.className = 'x-form-btn-td';
29096                 b.render(tr.appendChild(td));
29097             }
29098         }
29099         if(this.monitorValid){ // initialize after render
29100             this.startMonitoring();
29101         }
29102         this.fireEvent('rendered', this);
29103         return this;
29104     },
29105
29106     /**
29107      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29108      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29109      * object or a valid Roo.DomHelper element config
29110      * @param {Function} handler The function called when the button is clicked
29111      * @param {Object} scope (optional) The scope of the handler function
29112      * @return {Roo.Button}
29113      */
29114     addButton : function(config, handler, scope){
29115         var bc = {
29116             handler: handler,
29117             scope: scope,
29118             minWidth: this.minButtonWidth,
29119             hideParent:true
29120         };
29121         if(typeof config == "string"){
29122             bc.text = config;
29123         }else{
29124             Roo.apply(bc, config);
29125         }
29126         var btn = new Roo.Button(null, bc);
29127         this.buttons.push(btn);
29128         return btn;
29129     },
29130
29131      /**
29132      * Adds a series of form elements (using the xtype property as the factory method.
29133      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29134      * @param {Object} config 
29135      */
29136     
29137     addxtype : function()
29138     {
29139         var ar = Array.prototype.slice.call(arguments, 0);
29140         var ret = false;
29141         for(var i = 0; i < ar.length; i++) {
29142             if (!ar[i]) {
29143                 continue; // skip -- if this happends something invalid got sent, we 
29144                 // should ignore it, as basically that interface element will not show up
29145                 // and that should be pretty obvious!!
29146             }
29147             
29148             if (Roo.form[ar[i].xtype]) {
29149                 ar[i].form = this;
29150                 var fe = Roo.factory(ar[i], Roo.form);
29151                 if (!ret) {
29152                     ret = fe;
29153                 }
29154                 fe.form = this;
29155                 if (fe.store) {
29156                     fe.store.form = this;
29157                 }
29158                 if (fe.isLayout) {  
29159                          
29160                     this.start(fe);
29161                     this.allItems.push(fe);
29162                     if (fe.items && fe.addxtype) {
29163                         fe.addxtype.apply(fe, fe.items);
29164                         delete fe.items;
29165                     }
29166                      this.end();
29167                     continue;
29168                 }
29169                 
29170                 
29171                  
29172                 this.add(fe);
29173               //  console.log('adding ' + ar[i].xtype);
29174             }
29175             if (ar[i].xtype == 'Button') {  
29176                 //console.log('adding button');
29177                 //console.log(ar[i]);
29178                 this.addButton(ar[i]);
29179                 this.allItems.push(fe);
29180                 continue;
29181             }
29182             
29183             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29184                 alert('end is not supported on xtype any more, use items');
29185             //    this.end();
29186             //    //console.log('adding end');
29187             }
29188             
29189         }
29190         return ret;
29191     },
29192     
29193     /**
29194      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29195      * option "monitorValid"
29196      */
29197     startMonitoring : function(){
29198         if(!this.bound){
29199             this.bound = true;
29200             Roo.TaskMgr.start({
29201                 run : this.bindHandler,
29202                 interval : this.monitorPoll || 200,
29203                 scope: this
29204             });
29205         }
29206     },
29207
29208     /**
29209      * Stops monitoring of the valid state of this form
29210      */
29211     stopMonitoring : function(){
29212         this.bound = false;
29213     },
29214
29215     // private
29216     bindHandler : function(){
29217         if(!this.bound){
29218             return false; // stops binding
29219         }
29220         var valid = true;
29221         this.items.each(function(f){
29222             if(!f.isValid(true)){
29223                 valid = false;
29224                 return false;
29225             }
29226         });
29227         for(var i = 0, len = this.buttons.length; i < len; i++){
29228             var btn = this.buttons[i];
29229             if(btn.formBind === true && btn.disabled === valid){
29230                 btn.setDisabled(!valid);
29231             }
29232         }
29233         this.fireEvent('clientvalidation', this, valid);
29234     }
29235     
29236     
29237     
29238     
29239     
29240     
29241     
29242     
29243 });
29244
29245
29246 // back compat
29247 Roo.Form = Roo.form.Form;
29248 /*
29249  * Based on:
29250  * Ext JS Library 1.1.1
29251  * Copyright(c) 2006-2007, Ext JS, LLC.
29252  *
29253  * Originally Released Under LGPL - original licence link has changed is not relivant.
29254  *
29255  * Fork - LGPL
29256  * <script type="text/javascript">
29257  */
29258
29259 // as we use this in bootstrap.
29260 Roo.namespace('Roo.form');
29261  /**
29262  * @class Roo.form.Action
29263  * Internal Class used to handle form actions
29264  * @constructor
29265  * @param {Roo.form.BasicForm} el The form element or its id
29266  * @param {Object} config Configuration options
29267  */
29268
29269  
29270  
29271 // define the action interface
29272 Roo.form.Action = function(form, options){
29273     this.form = form;
29274     this.options = options || {};
29275 };
29276 /**
29277  * Client Validation Failed
29278  * @const 
29279  */
29280 Roo.form.Action.CLIENT_INVALID = 'client';
29281 /**
29282  * Server Validation Failed
29283  * @const 
29284  */
29285 Roo.form.Action.SERVER_INVALID = 'server';
29286  /**
29287  * Connect to Server Failed
29288  * @const 
29289  */
29290 Roo.form.Action.CONNECT_FAILURE = 'connect';
29291 /**
29292  * Reading Data from Server Failed
29293  * @const 
29294  */
29295 Roo.form.Action.LOAD_FAILURE = 'load';
29296
29297 Roo.form.Action.prototype = {
29298     type : 'default',
29299     failureType : undefined,
29300     response : undefined,
29301     result : undefined,
29302
29303     // interface method
29304     run : function(options){
29305
29306     },
29307
29308     // interface method
29309     success : function(response){
29310
29311     },
29312
29313     // interface method
29314     handleResponse : function(response){
29315
29316     },
29317
29318     // default connection failure
29319     failure : function(response){
29320         
29321         this.response = response;
29322         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29323         this.form.afterAction(this, false);
29324     },
29325
29326     processResponse : function(response){
29327         this.response = response;
29328         if(!response.responseText){
29329             return true;
29330         }
29331         this.result = this.handleResponse(response);
29332         return this.result;
29333     },
29334
29335     // utility functions used internally
29336     getUrl : function(appendParams){
29337         var url = this.options.url || this.form.url || this.form.el.dom.action;
29338         if(appendParams){
29339             var p = this.getParams();
29340             if(p){
29341                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29342             }
29343         }
29344         return url;
29345     },
29346
29347     getMethod : function(){
29348         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29349     },
29350
29351     getParams : function(){
29352         var bp = this.form.baseParams;
29353         var p = this.options.params;
29354         if(p){
29355             if(typeof p == "object"){
29356                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29357             }else if(typeof p == 'string' && bp){
29358                 p += '&' + Roo.urlEncode(bp);
29359             }
29360         }else if(bp){
29361             p = Roo.urlEncode(bp);
29362         }
29363         return p;
29364     },
29365
29366     createCallback : function(){
29367         return {
29368             success: this.success,
29369             failure: this.failure,
29370             scope: this,
29371             timeout: (this.form.timeout*1000),
29372             upload: this.form.fileUpload ? this.success : undefined
29373         };
29374     }
29375 };
29376
29377 Roo.form.Action.Submit = function(form, options){
29378     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29379 };
29380
29381 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29382     type : 'submit',
29383
29384     haveProgress : false,
29385     uploadComplete : false,
29386     
29387     // uploadProgress indicator.
29388     uploadProgress : function()
29389     {
29390         if (!this.form.progressUrl) {
29391             return;
29392         }
29393         
29394         if (!this.haveProgress) {
29395             Roo.MessageBox.progress("Uploading", "Uploading");
29396         }
29397         if (this.uploadComplete) {
29398            Roo.MessageBox.hide();
29399            return;
29400         }
29401         
29402         this.haveProgress = true;
29403    
29404         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29405         
29406         var c = new Roo.data.Connection();
29407         c.request({
29408             url : this.form.progressUrl,
29409             params: {
29410                 id : uid
29411             },
29412             method: 'GET',
29413             success : function(req){
29414                //console.log(data);
29415                 var rdata = false;
29416                 var edata;
29417                 try  {
29418                    rdata = Roo.decode(req.responseText)
29419                 } catch (e) {
29420                     Roo.log("Invalid data from server..");
29421                     Roo.log(edata);
29422                     return;
29423                 }
29424                 if (!rdata || !rdata.success) {
29425                     Roo.log(rdata);
29426                     Roo.MessageBox.alert(Roo.encode(rdata));
29427                     return;
29428                 }
29429                 var data = rdata.data;
29430                 
29431                 if (this.uploadComplete) {
29432                    Roo.MessageBox.hide();
29433                    return;
29434                 }
29435                    
29436                 if (data){
29437                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29438                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29439                     );
29440                 }
29441                 this.uploadProgress.defer(2000,this);
29442             },
29443        
29444             failure: function(data) {
29445                 Roo.log('progress url failed ');
29446                 Roo.log(data);
29447             },
29448             scope : this
29449         });
29450            
29451     },
29452     
29453     
29454     run : function()
29455     {
29456         // run get Values on the form, so it syncs any secondary forms.
29457         this.form.getValues();
29458         
29459         var o = this.options;
29460         var method = this.getMethod();
29461         var isPost = method == 'POST';
29462         if(o.clientValidation === false || this.form.isValid()){
29463             
29464             if (this.form.progressUrl) {
29465                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29466                     (new Date() * 1) + '' + Math.random());
29467                     
29468             } 
29469             
29470             
29471             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29472                 form:this.form.el.dom,
29473                 url:this.getUrl(!isPost),
29474                 method: method,
29475                 params:isPost ? this.getParams() : null,
29476                 isUpload: this.form.fileUpload
29477             }));
29478             
29479             this.uploadProgress();
29480
29481         }else if (o.clientValidation !== false){ // client validation failed
29482             this.failureType = Roo.form.Action.CLIENT_INVALID;
29483             this.form.afterAction(this, false);
29484         }
29485     },
29486
29487     success : function(response)
29488     {
29489         this.uploadComplete= true;
29490         if (this.haveProgress) {
29491             Roo.MessageBox.hide();
29492         }
29493         
29494         
29495         var result = this.processResponse(response);
29496         if(result === true || result.success){
29497             this.form.afterAction(this, true);
29498             return;
29499         }
29500         if(result.errors){
29501             this.form.markInvalid(result.errors);
29502             this.failureType = Roo.form.Action.SERVER_INVALID;
29503         }
29504         this.form.afterAction(this, false);
29505     },
29506     failure : function(response)
29507     {
29508         this.uploadComplete= true;
29509         if (this.haveProgress) {
29510             Roo.MessageBox.hide();
29511         }
29512         
29513         this.response = response;
29514         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29515         this.form.afterAction(this, false);
29516     },
29517     
29518     handleResponse : function(response){
29519         if(this.form.errorReader){
29520             var rs = this.form.errorReader.read(response);
29521             var errors = [];
29522             if(rs.records){
29523                 for(var i = 0, len = rs.records.length; i < len; i++) {
29524                     var r = rs.records[i];
29525                     errors[i] = r.data;
29526                 }
29527             }
29528             if(errors.length < 1){
29529                 errors = null;
29530             }
29531             return {
29532                 success : rs.success,
29533                 errors : errors
29534             };
29535         }
29536         var ret = false;
29537         try {
29538             ret = Roo.decode(response.responseText);
29539         } catch (e) {
29540             ret = {
29541                 success: false,
29542                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29543                 errors : []
29544             };
29545         }
29546         return ret;
29547         
29548     }
29549 });
29550
29551
29552 Roo.form.Action.Load = function(form, options){
29553     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29554     this.reader = this.form.reader;
29555 };
29556
29557 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29558     type : 'load',
29559
29560     run : function(){
29561         
29562         Roo.Ajax.request(Roo.apply(
29563                 this.createCallback(), {
29564                     method:this.getMethod(),
29565                     url:this.getUrl(false),
29566                     params:this.getParams()
29567         }));
29568     },
29569
29570     success : function(response){
29571         
29572         var result = this.processResponse(response);
29573         if(result === true || !result.success || !result.data){
29574             this.failureType = Roo.form.Action.LOAD_FAILURE;
29575             this.form.afterAction(this, false);
29576             return;
29577         }
29578         this.form.clearInvalid();
29579         this.form.setValues(result.data);
29580         this.form.afterAction(this, true);
29581     },
29582
29583     handleResponse : function(response){
29584         if(this.form.reader){
29585             var rs = this.form.reader.read(response);
29586             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29587             return {
29588                 success : rs.success,
29589                 data : data
29590             };
29591         }
29592         return Roo.decode(response.responseText);
29593     }
29594 });
29595
29596 Roo.form.Action.ACTION_TYPES = {
29597     'load' : Roo.form.Action.Load,
29598     'submit' : Roo.form.Action.Submit
29599 };/*
29600  * Based on:
29601  * Ext JS Library 1.1.1
29602  * Copyright(c) 2006-2007, Ext JS, LLC.
29603  *
29604  * Originally Released Under LGPL - original licence link has changed is not relivant.
29605  *
29606  * Fork - LGPL
29607  * <script type="text/javascript">
29608  */
29609  
29610 /**
29611  * @class Roo.form.Layout
29612  * @extends Roo.Component
29613  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29614  * @constructor
29615  * @param {Object} config Configuration options
29616  */
29617 Roo.form.Layout = function(config){
29618     var xitems = [];
29619     if (config.items) {
29620         xitems = config.items;
29621         delete config.items;
29622     }
29623     Roo.form.Layout.superclass.constructor.call(this, config);
29624     this.stack = [];
29625     Roo.each(xitems, this.addxtype, this);
29626      
29627 };
29628
29629 Roo.extend(Roo.form.Layout, Roo.Component, {
29630     /**
29631      * @cfg {String/Object} autoCreate
29632      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29633      */
29634     /**
29635      * @cfg {String/Object/Function} style
29636      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29637      * a function which returns such a specification.
29638      */
29639     /**
29640      * @cfg {String} labelAlign
29641      * Valid values are "left," "top" and "right" (defaults to "left")
29642      */
29643     /**
29644      * @cfg {Number} labelWidth
29645      * Fixed width in pixels of all field labels (defaults to undefined)
29646      */
29647     /**
29648      * @cfg {Boolean} clear
29649      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29650      */
29651     clear : true,
29652     /**
29653      * @cfg {String} labelSeparator
29654      * The separator to use after field labels (defaults to ':')
29655      */
29656     labelSeparator : ':',
29657     /**
29658      * @cfg {Boolean} hideLabels
29659      * True to suppress the display of field labels in this layout (defaults to false)
29660      */
29661     hideLabels : false,
29662
29663     // private
29664     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29665     
29666     isLayout : true,
29667     
29668     // private
29669     onRender : function(ct, position){
29670         if(this.el){ // from markup
29671             this.el = Roo.get(this.el);
29672         }else {  // generate
29673             var cfg = this.getAutoCreate();
29674             this.el = ct.createChild(cfg, position);
29675         }
29676         if(this.style){
29677             this.el.applyStyles(this.style);
29678         }
29679         if(this.labelAlign){
29680             this.el.addClass('x-form-label-'+this.labelAlign);
29681         }
29682         if(this.hideLabels){
29683             this.labelStyle = "display:none";
29684             this.elementStyle = "padding-left:0;";
29685         }else{
29686             if(typeof this.labelWidth == 'number'){
29687                 this.labelStyle = "width:"+this.labelWidth+"px;";
29688                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29689             }
29690             if(this.labelAlign == 'top'){
29691                 this.labelStyle = "width:auto;";
29692                 this.elementStyle = "padding-left:0;";
29693             }
29694         }
29695         var stack = this.stack;
29696         var slen = stack.length;
29697         if(slen > 0){
29698             if(!this.fieldTpl){
29699                 var t = new Roo.Template(
29700                     '<div class="x-form-item {5}">',
29701                         '<label for="{0}" style="{2}">{1}{4}</label>',
29702                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29703                         '</div>',
29704                     '</div><div class="x-form-clear-left"></div>'
29705                 );
29706                 t.disableFormats = true;
29707                 t.compile();
29708                 Roo.form.Layout.prototype.fieldTpl = t;
29709             }
29710             for(var i = 0; i < slen; i++) {
29711                 if(stack[i].isFormField){
29712                     this.renderField(stack[i]);
29713                 }else{
29714                     this.renderComponent(stack[i]);
29715                 }
29716             }
29717         }
29718         if(this.clear){
29719             this.el.createChild({cls:'x-form-clear'});
29720         }
29721     },
29722
29723     // private
29724     renderField : function(f){
29725         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29726                f.id, //0
29727                f.fieldLabel, //1
29728                f.labelStyle||this.labelStyle||'', //2
29729                this.elementStyle||'', //3
29730                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29731                f.itemCls||this.itemCls||''  //5
29732        ], true).getPrevSibling());
29733     },
29734
29735     // private
29736     renderComponent : function(c){
29737         c.render(c.isLayout ? this.el : this.el.createChild());    
29738     },
29739     /**
29740      * Adds a object form elements (using the xtype property as the factory method.)
29741      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29742      * @param {Object} config 
29743      */
29744     addxtype : function(o)
29745     {
29746         // create the lement.
29747         o.form = this.form;
29748         var fe = Roo.factory(o, Roo.form);
29749         this.form.allItems.push(fe);
29750         this.stack.push(fe);
29751         
29752         if (fe.isFormField) {
29753             this.form.items.add(fe);
29754         }
29755          
29756         return fe;
29757     }
29758 });
29759
29760 /**
29761  * @class Roo.form.Column
29762  * @extends Roo.form.Layout
29763  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29764  * @constructor
29765  * @param {Object} config Configuration options
29766  */
29767 Roo.form.Column = function(config){
29768     Roo.form.Column.superclass.constructor.call(this, config);
29769 };
29770
29771 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29772     /**
29773      * @cfg {Number/String} width
29774      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29775      */
29776     /**
29777      * @cfg {String/Object} autoCreate
29778      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29779      */
29780
29781     // private
29782     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29783
29784     // private
29785     onRender : function(ct, position){
29786         Roo.form.Column.superclass.onRender.call(this, ct, position);
29787         if(this.width){
29788             this.el.setWidth(this.width);
29789         }
29790     }
29791 });
29792
29793
29794 /**
29795  * @class Roo.form.Row
29796  * @extends Roo.form.Layout
29797  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29798  * @constructor
29799  * @param {Object} config Configuration options
29800  */
29801
29802  
29803 Roo.form.Row = function(config){
29804     Roo.form.Row.superclass.constructor.call(this, config);
29805 };
29806  
29807 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29808       /**
29809      * @cfg {Number/String} width
29810      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29811      */
29812     /**
29813      * @cfg {Number/String} height
29814      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29815      */
29816     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29817     
29818     padWidth : 20,
29819     // private
29820     onRender : function(ct, position){
29821         //console.log('row render');
29822         if(!this.rowTpl){
29823             var t = new Roo.Template(
29824                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29825                     '<label for="{0}" style="{2}">{1}{4}</label>',
29826                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29827                     '</div>',
29828                 '</div>'
29829             );
29830             t.disableFormats = true;
29831             t.compile();
29832             Roo.form.Layout.prototype.rowTpl = t;
29833         }
29834         this.fieldTpl = this.rowTpl;
29835         
29836         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29837         var labelWidth = 100;
29838         
29839         if ((this.labelAlign != 'top')) {
29840             if (typeof this.labelWidth == 'number') {
29841                 labelWidth = this.labelWidth
29842             }
29843             this.padWidth =  20 + labelWidth;
29844             
29845         }
29846         
29847         Roo.form.Column.superclass.onRender.call(this, ct, position);
29848         if(this.width){
29849             this.el.setWidth(this.width);
29850         }
29851         if(this.height){
29852             this.el.setHeight(this.height);
29853         }
29854     },
29855     
29856     // private
29857     renderField : function(f){
29858         f.fieldEl = this.fieldTpl.append(this.el, [
29859                f.id, f.fieldLabel,
29860                f.labelStyle||this.labelStyle||'',
29861                this.elementStyle||'',
29862                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29863                f.itemCls||this.itemCls||'',
29864                f.width ? f.width + this.padWidth : 160 + this.padWidth
29865        ],true);
29866     }
29867 });
29868  
29869
29870 /**
29871  * @class Roo.form.FieldSet
29872  * @extends Roo.form.Layout
29873  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29874  * @constructor
29875  * @param {Object} config Configuration options
29876  */
29877 Roo.form.FieldSet = function(config){
29878     Roo.form.FieldSet.superclass.constructor.call(this, config);
29879 };
29880
29881 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29882     /**
29883      * @cfg {String} legend
29884      * The text to display as the legend for the FieldSet (defaults to '')
29885      */
29886     /**
29887      * @cfg {String/Object} autoCreate
29888      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29889      */
29890
29891     // private
29892     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29893
29894     // private
29895     onRender : function(ct, position){
29896         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29897         if(this.legend){
29898             this.setLegend(this.legend);
29899         }
29900     },
29901
29902     // private
29903     setLegend : function(text){
29904         if(this.rendered){
29905             this.el.child('legend').update(text);
29906         }
29907     }
29908 });/*
29909  * Based on:
29910  * Ext JS Library 1.1.1
29911  * Copyright(c) 2006-2007, Ext JS, LLC.
29912  *
29913  * Originally Released Under LGPL - original licence link has changed is not relivant.
29914  *
29915  * Fork - LGPL
29916  * <script type="text/javascript">
29917  */
29918 /**
29919  * @class Roo.form.VTypes
29920  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29921  * @singleton
29922  */
29923 Roo.form.VTypes = function(){
29924     // closure these in so they are only created once.
29925     var alpha = /^[a-zA-Z_]+$/;
29926     var alphanum = /^[a-zA-Z0-9_]+$/;
29927     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29928     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29929
29930     // All these messages and functions are configurable
29931     return {
29932         /**
29933          * The function used to validate email addresses
29934          * @param {String} value The email address
29935          */
29936         'email' : function(v){
29937             return email.test(v);
29938         },
29939         /**
29940          * The error text to display when the email validation function returns false
29941          * @type String
29942          */
29943         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29944         /**
29945          * The keystroke filter mask to be applied on email input
29946          * @type RegExp
29947          */
29948         'emailMask' : /[a-z0-9_\.\-@]/i,
29949
29950         /**
29951          * The function used to validate URLs
29952          * @param {String} value The URL
29953          */
29954         'url' : function(v){
29955             return url.test(v);
29956         },
29957         /**
29958          * The error text to display when the url validation function returns false
29959          * @type String
29960          */
29961         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29962         
29963         /**
29964          * The function used to validate alpha values
29965          * @param {String} value The value
29966          */
29967         'alpha' : function(v){
29968             return alpha.test(v);
29969         },
29970         /**
29971          * The error text to display when the alpha validation function returns false
29972          * @type String
29973          */
29974         'alphaText' : 'This field should only contain letters and _',
29975         /**
29976          * The keystroke filter mask to be applied on alpha input
29977          * @type RegExp
29978          */
29979         'alphaMask' : /[a-z_]/i,
29980
29981         /**
29982          * The function used to validate alphanumeric values
29983          * @param {String} value The value
29984          */
29985         'alphanum' : function(v){
29986             return alphanum.test(v);
29987         },
29988         /**
29989          * The error text to display when the alphanumeric validation function returns false
29990          * @type String
29991          */
29992         'alphanumText' : 'This field should only contain letters, numbers and _',
29993         /**
29994          * The keystroke filter mask to be applied on alphanumeric input
29995          * @type RegExp
29996          */
29997         'alphanumMask' : /[a-z0-9_]/i
29998     };
29999 }();//<script type="text/javascript">
30000
30001 /**
30002  * @class Roo.form.FCKeditor
30003  * @extends Roo.form.TextArea
30004  * Wrapper around the FCKEditor http://www.fckeditor.net
30005  * @constructor
30006  * Creates a new FCKeditor
30007  * @param {Object} config Configuration options
30008  */
30009 Roo.form.FCKeditor = function(config){
30010     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30011     this.addEvents({
30012          /**
30013          * @event editorinit
30014          * Fired when the editor is initialized - you can add extra handlers here..
30015          * @param {FCKeditor} this
30016          * @param {Object} the FCK object.
30017          */
30018         editorinit : true
30019     });
30020     
30021     
30022 };
30023 Roo.form.FCKeditor.editors = { };
30024 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30025 {
30026     //defaultAutoCreate : {
30027     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30028     //},
30029     // private
30030     /**
30031      * @cfg {Object} fck options - see fck manual for details.
30032      */
30033     fckconfig : false,
30034     
30035     /**
30036      * @cfg {Object} fck toolbar set (Basic or Default)
30037      */
30038     toolbarSet : 'Basic',
30039     /**
30040      * @cfg {Object} fck BasePath
30041      */ 
30042     basePath : '/fckeditor/',
30043     
30044     
30045     frame : false,
30046     
30047     value : '',
30048     
30049    
30050     onRender : function(ct, position)
30051     {
30052         if(!this.el){
30053             this.defaultAutoCreate = {
30054                 tag: "textarea",
30055                 style:"width:300px;height:60px;",
30056                 autocomplete: "off"
30057             };
30058         }
30059         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30060         /*
30061         if(this.grow){
30062             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30063             if(this.preventScrollbars){
30064                 this.el.setStyle("overflow", "hidden");
30065             }
30066             this.el.setHeight(this.growMin);
30067         }
30068         */
30069         //console.log('onrender' + this.getId() );
30070         Roo.form.FCKeditor.editors[this.getId()] = this;
30071          
30072
30073         this.replaceTextarea() ;
30074         
30075     },
30076     
30077     getEditor : function() {
30078         return this.fckEditor;
30079     },
30080     /**
30081      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30082      * @param {Mixed} value The value to set
30083      */
30084     
30085     
30086     setValue : function(value)
30087     {
30088         //console.log('setValue: ' + value);
30089         
30090         if(typeof(value) == 'undefined') { // not sure why this is happending...
30091             return;
30092         }
30093         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30094         
30095         //if(!this.el || !this.getEditor()) {
30096         //    this.value = value;
30097             //this.setValue.defer(100,this,[value]);    
30098         //    return;
30099         //} 
30100         
30101         if(!this.getEditor()) {
30102             return;
30103         }
30104         
30105         this.getEditor().SetData(value);
30106         
30107         //
30108
30109     },
30110
30111     /**
30112      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30113      * @return {Mixed} value The field value
30114      */
30115     getValue : function()
30116     {
30117         
30118         if (this.frame && this.frame.dom.style.display == 'none') {
30119             return Roo.form.FCKeditor.superclass.getValue.call(this);
30120         }
30121         
30122         if(!this.el || !this.getEditor()) {
30123            
30124            // this.getValue.defer(100,this); 
30125             return this.value;
30126         }
30127        
30128         
30129         var value=this.getEditor().GetData();
30130         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30131         return Roo.form.FCKeditor.superclass.getValue.call(this);
30132         
30133
30134     },
30135
30136     /**
30137      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30138      * @return {Mixed} value The field value
30139      */
30140     getRawValue : function()
30141     {
30142         if (this.frame && this.frame.dom.style.display == 'none') {
30143             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30144         }
30145         
30146         if(!this.el || !this.getEditor()) {
30147             //this.getRawValue.defer(100,this); 
30148             return this.value;
30149             return;
30150         }
30151         
30152         
30153         
30154         var value=this.getEditor().GetData();
30155         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30156         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30157          
30158     },
30159     
30160     setSize : function(w,h) {
30161         
30162         
30163         
30164         //if (this.frame && this.frame.dom.style.display == 'none') {
30165         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30166         //    return;
30167         //}
30168         //if(!this.el || !this.getEditor()) {
30169         //    this.setSize.defer(100,this, [w,h]); 
30170         //    return;
30171         //}
30172         
30173         
30174         
30175         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30176         
30177         this.frame.dom.setAttribute('width', w);
30178         this.frame.dom.setAttribute('height', h);
30179         this.frame.setSize(w,h);
30180         
30181     },
30182     
30183     toggleSourceEdit : function(value) {
30184         
30185       
30186          
30187         this.el.dom.style.display = value ? '' : 'none';
30188         this.frame.dom.style.display = value ?  'none' : '';
30189         
30190     },
30191     
30192     
30193     focus: function(tag)
30194     {
30195         if (this.frame.dom.style.display == 'none') {
30196             return Roo.form.FCKeditor.superclass.focus.call(this);
30197         }
30198         if(!this.el || !this.getEditor()) {
30199             this.focus.defer(100,this, [tag]); 
30200             return;
30201         }
30202         
30203         
30204         
30205         
30206         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30207         this.getEditor().Focus();
30208         if (tgs.length) {
30209             if (!this.getEditor().Selection.GetSelection()) {
30210                 this.focus.defer(100,this, [tag]); 
30211                 return;
30212             }
30213             
30214             
30215             var r = this.getEditor().EditorDocument.createRange();
30216             r.setStart(tgs[0],0);
30217             r.setEnd(tgs[0],0);
30218             this.getEditor().Selection.GetSelection().removeAllRanges();
30219             this.getEditor().Selection.GetSelection().addRange(r);
30220             this.getEditor().Focus();
30221         }
30222         
30223     },
30224     
30225     
30226     
30227     replaceTextarea : function()
30228     {
30229         if ( document.getElementById( this.getId() + '___Frame' ) )
30230             return ;
30231         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30232         //{
30233             // We must check the elements firstly using the Id and then the name.
30234         var oTextarea = document.getElementById( this.getId() );
30235         
30236         var colElementsByName = document.getElementsByName( this.getId() ) ;
30237          
30238         oTextarea.style.display = 'none' ;
30239
30240         if ( oTextarea.tabIndex ) {            
30241             this.TabIndex = oTextarea.tabIndex ;
30242         }
30243         
30244         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30245         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30246         this.frame = Roo.get(this.getId() + '___Frame')
30247     },
30248     
30249     _getConfigHtml : function()
30250     {
30251         var sConfig = '' ;
30252
30253         for ( var o in this.fckconfig ) {
30254             sConfig += sConfig.length > 0  ? '&amp;' : '';
30255             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30256         }
30257
30258         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30259     },
30260     
30261     
30262     _getIFrameHtml : function()
30263     {
30264         var sFile = 'fckeditor.html' ;
30265         /* no idea what this is about..
30266         try
30267         {
30268             if ( (/fcksource=true/i).test( window.top.location.search ) )
30269                 sFile = 'fckeditor.original.html' ;
30270         }
30271         catch (e) { 
30272         */
30273
30274         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30275         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30276         
30277         
30278         var html = '<iframe id="' + this.getId() +
30279             '___Frame" src="' + sLink +
30280             '" width="' + this.width +
30281             '" height="' + this.height + '"' +
30282             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30283             ' frameborder="0" scrolling="no"></iframe>' ;
30284
30285         return html ;
30286     },
30287     
30288     _insertHtmlBefore : function( html, element )
30289     {
30290         if ( element.insertAdjacentHTML )       {
30291             // IE
30292             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30293         } else { // Gecko
30294             var oRange = document.createRange() ;
30295             oRange.setStartBefore( element ) ;
30296             var oFragment = oRange.createContextualFragment( html );
30297             element.parentNode.insertBefore( oFragment, element ) ;
30298         }
30299     }
30300     
30301     
30302   
30303     
30304     
30305     
30306     
30307
30308 });
30309
30310 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30311
30312 function FCKeditor_OnComplete(editorInstance){
30313     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30314     f.fckEditor = editorInstance;
30315     //console.log("loaded");
30316     f.fireEvent('editorinit', f, editorInstance);
30317
30318   
30319
30320  
30321
30322
30323
30324
30325
30326
30327
30328
30329
30330
30331
30332
30333
30334
30335
30336 //<script type="text/javascript">
30337 /**
30338  * @class Roo.form.GridField
30339  * @extends Roo.form.Field
30340  * Embed a grid (or editable grid into a form)
30341  * STATUS ALPHA
30342  * 
30343  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30344  * it needs 
30345  * xgrid.store = Roo.data.Store
30346  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30347  * xgrid.store.reader = Roo.data.JsonReader 
30348  * 
30349  * 
30350  * @constructor
30351  * Creates a new GridField
30352  * @param {Object} config Configuration options
30353  */
30354 Roo.form.GridField = function(config){
30355     Roo.form.GridField.superclass.constructor.call(this, config);
30356      
30357 };
30358
30359 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30360     /**
30361      * @cfg {Number} width  - used to restrict width of grid..
30362      */
30363     width : 100,
30364     /**
30365      * @cfg {Number} height - used to restrict height of grid..
30366      */
30367     height : 50,
30368      /**
30369      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30370          * 
30371          *}
30372      */
30373     xgrid : false, 
30374     /**
30375      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30376      * {tag: "input", type: "checkbox", autocomplete: "off"})
30377      */
30378    // defaultAutoCreate : { tag: 'div' },
30379     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30380     /**
30381      * @cfg {String} addTitle Text to include for adding a title.
30382      */
30383     addTitle : false,
30384     //
30385     onResize : function(){
30386         Roo.form.Field.superclass.onResize.apply(this, arguments);
30387     },
30388
30389     initEvents : function(){
30390         // Roo.form.Checkbox.superclass.initEvents.call(this);
30391         // has no events...
30392        
30393     },
30394
30395
30396     getResizeEl : function(){
30397         return this.wrap;
30398     },
30399
30400     getPositionEl : function(){
30401         return this.wrap;
30402     },
30403
30404     // private
30405     onRender : function(ct, position){
30406         
30407         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30408         var style = this.style;
30409         delete this.style;
30410         
30411         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30412         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30413         this.viewEl = this.wrap.createChild({ tag: 'div' });
30414         if (style) {
30415             this.viewEl.applyStyles(style);
30416         }
30417         if (this.width) {
30418             this.viewEl.setWidth(this.width);
30419         }
30420         if (this.height) {
30421             this.viewEl.setHeight(this.height);
30422         }
30423         //if(this.inputValue !== undefined){
30424         //this.setValue(this.value);
30425         
30426         
30427         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30428         
30429         
30430         this.grid.render();
30431         this.grid.getDataSource().on('remove', this.refreshValue, this);
30432         this.grid.getDataSource().on('update', this.refreshValue, this);
30433         this.grid.on('afteredit', this.refreshValue, this);
30434  
30435     },
30436      
30437     
30438     /**
30439      * Sets the value of the item. 
30440      * @param {String} either an object  or a string..
30441      */
30442     setValue : function(v){
30443         //this.value = v;
30444         v = v || []; // empty set..
30445         // this does not seem smart - it really only affects memoryproxy grids..
30446         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30447             var ds = this.grid.getDataSource();
30448             // assumes a json reader..
30449             var data = {}
30450             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30451             ds.loadData( data);
30452         }
30453         // clear selection so it does not get stale.
30454         if (this.grid.sm) { 
30455             this.grid.sm.clearSelections();
30456         }
30457         
30458         Roo.form.GridField.superclass.setValue.call(this, v);
30459         this.refreshValue();
30460         // should load data in the grid really....
30461     },
30462     
30463     // private
30464     refreshValue: function() {
30465          var val = [];
30466         this.grid.getDataSource().each(function(r) {
30467             val.push(r.data);
30468         });
30469         this.el.dom.value = Roo.encode(val);
30470     }
30471     
30472      
30473     
30474     
30475 });/*
30476  * Based on:
30477  * Ext JS Library 1.1.1
30478  * Copyright(c) 2006-2007, Ext JS, LLC.
30479  *
30480  * Originally Released Under LGPL - original licence link has changed is not relivant.
30481  *
30482  * Fork - LGPL
30483  * <script type="text/javascript">
30484  */
30485 /**
30486  * @class Roo.form.DisplayField
30487  * @extends Roo.form.Field
30488  * A generic Field to display non-editable data.
30489  * @constructor
30490  * Creates a new Display Field item.
30491  * @param {Object} config Configuration options
30492  */
30493 Roo.form.DisplayField = function(config){
30494     Roo.form.DisplayField.superclass.constructor.call(this, config);
30495     
30496 };
30497
30498 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30499     inputType:      'hidden',
30500     allowBlank:     true,
30501     readOnly:         true,
30502     
30503  
30504     /**
30505      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30506      */
30507     focusClass : undefined,
30508     /**
30509      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30510      */
30511     fieldClass: 'x-form-field',
30512     
30513      /**
30514      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30515      */
30516     valueRenderer: undefined,
30517     
30518     width: 100,
30519     /**
30520      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30521      * {tag: "input", type: "checkbox", autocomplete: "off"})
30522      */
30523      
30524  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30525
30526     onResize : function(){
30527         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30528         
30529     },
30530
30531     initEvents : function(){
30532         // Roo.form.Checkbox.superclass.initEvents.call(this);
30533         // has no events...
30534        
30535     },
30536
30537
30538     getResizeEl : function(){
30539         return this.wrap;
30540     },
30541
30542     getPositionEl : function(){
30543         return this.wrap;
30544     },
30545
30546     // private
30547     onRender : function(ct, position){
30548         
30549         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30550         //if(this.inputValue !== undefined){
30551         this.wrap = this.el.wrap();
30552         
30553         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30554         
30555         if (this.bodyStyle) {
30556             this.viewEl.applyStyles(this.bodyStyle);
30557         }
30558         //this.viewEl.setStyle('padding', '2px');
30559         
30560         this.setValue(this.value);
30561         
30562     },
30563 /*
30564     // private
30565     initValue : Roo.emptyFn,
30566
30567   */
30568
30569         // private
30570     onClick : function(){
30571         
30572     },
30573
30574     /**
30575      * Sets the checked state of the checkbox.
30576      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30577      */
30578     setValue : function(v){
30579         this.value = v;
30580         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30581         // this might be called before we have a dom element..
30582         if (!this.viewEl) {
30583             return;
30584         }
30585         this.viewEl.dom.innerHTML = html;
30586         Roo.form.DisplayField.superclass.setValue.call(this, v);
30587
30588     }
30589 });/*
30590  * 
30591  * Licence- LGPL
30592  * 
30593  */
30594
30595 /**
30596  * @class Roo.form.DayPicker
30597  * @extends Roo.form.Field
30598  * A Day picker show [M] [T] [W] ....
30599  * @constructor
30600  * Creates a new Day Picker
30601  * @param {Object} config Configuration options
30602  */
30603 Roo.form.DayPicker= function(config){
30604     Roo.form.DayPicker.superclass.constructor.call(this, config);
30605      
30606 };
30607
30608 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30609     /**
30610      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30611      */
30612     focusClass : undefined,
30613     /**
30614      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30615      */
30616     fieldClass: "x-form-field",
30617    
30618     /**
30619      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30620      * {tag: "input", type: "checkbox", autocomplete: "off"})
30621      */
30622     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30623     
30624    
30625     actionMode : 'viewEl', 
30626     //
30627     // private
30628  
30629     inputType : 'hidden',
30630     
30631      
30632     inputElement: false, // real input element?
30633     basedOn: false, // ????
30634     
30635     isFormField: true, // not sure where this is needed!!!!
30636
30637     onResize : function(){
30638         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30639         if(!this.boxLabel){
30640             this.el.alignTo(this.wrap, 'c-c');
30641         }
30642     },
30643
30644     initEvents : function(){
30645         Roo.form.Checkbox.superclass.initEvents.call(this);
30646         this.el.on("click", this.onClick,  this);
30647         this.el.on("change", this.onClick,  this);
30648     },
30649
30650
30651     getResizeEl : function(){
30652         return this.wrap;
30653     },
30654
30655     getPositionEl : function(){
30656         return this.wrap;
30657     },
30658
30659     
30660     // private
30661     onRender : function(ct, position){
30662         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30663        
30664         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30665         
30666         var r1 = '<table><tr>';
30667         var r2 = '<tr class="x-form-daypick-icons">';
30668         for (var i=0; i < 7; i++) {
30669             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30670             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30671         }
30672         
30673         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30674         viewEl.select('img').on('click', this.onClick, this);
30675         this.viewEl = viewEl;   
30676         
30677         
30678         // this will not work on Chrome!!!
30679         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30680         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30681         
30682         
30683           
30684
30685     },
30686
30687     // private
30688     initValue : Roo.emptyFn,
30689
30690     /**
30691      * Returns the checked state of the checkbox.
30692      * @return {Boolean} True if checked, else false
30693      */
30694     getValue : function(){
30695         return this.el.dom.value;
30696         
30697     },
30698
30699         // private
30700     onClick : function(e){ 
30701         //this.setChecked(!this.checked);
30702         Roo.get(e.target).toggleClass('x-menu-item-checked');
30703         this.refreshValue();
30704         //if(this.el.dom.checked != this.checked){
30705         //    this.setValue(this.el.dom.checked);
30706        // }
30707     },
30708     
30709     // private
30710     refreshValue : function()
30711     {
30712         var val = '';
30713         this.viewEl.select('img',true).each(function(e,i,n)  {
30714             val += e.is(".x-menu-item-checked") ? String(n) : '';
30715         });
30716         this.setValue(val, true);
30717     },
30718
30719     /**
30720      * Sets the checked state of the checkbox.
30721      * On is always based on a string comparison between inputValue and the param.
30722      * @param {Boolean/String} value - the value to set 
30723      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30724      */
30725     setValue : function(v,suppressEvent){
30726         if (!this.el.dom) {
30727             return;
30728         }
30729         var old = this.el.dom.value ;
30730         this.el.dom.value = v;
30731         if (suppressEvent) {
30732             return ;
30733         }
30734          
30735         // update display..
30736         this.viewEl.select('img',true).each(function(e,i,n)  {
30737             
30738             var on = e.is(".x-menu-item-checked");
30739             var newv = v.indexOf(String(n)) > -1;
30740             if (on != newv) {
30741                 e.toggleClass('x-menu-item-checked');
30742             }
30743             
30744         });
30745         
30746         
30747         this.fireEvent('change', this, v, old);
30748         
30749         
30750     },
30751    
30752     // handle setting of hidden value by some other method!!?!?
30753     setFromHidden: function()
30754     {
30755         if(!this.el){
30756             return;
30757         }
30758         //console.log("SET FROM HIDDEN");
30759         //alert('setFrom hidden');
30760         this.setValue(this.el.dom.value);
30761     },
30762     
30763     onDestroy : function()
30764     {
30765         if(this.viewEl){
30766             Roo.get(this.viewEl).remove();
30767         }
30768          
30769         Roo.form.DayPicker.superclass.onDestroy.call(this);
30770     }
30771
30772 });/*
30773  * RooJS Library 1.1.1
30774  * Copyright(c) 2008-2011  Alan Knowles
30775  *
30776  * License - LGPL
30777  */
30778  
30779
30780 /**
30781  * @class Roo.form.ComboCheck
30782  * @extends Roo.form.ComboBox
30783  * A combobox for multiple select items.
30784  *
30785  * FIXME - could do with a reset button..
30786  * 
30787  * @constructor
30788  * Create a new ComboCheck
30789  * @param {Object} config Configuration options
30790  */
30791 Roo.form.ComboCheck = function(config){
30792     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30793     // should verify some data...
30794     // like
30795     // hiddenName = required..
30796     // displayField = required
30797     // valudField == required
30798     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30799     var _t = this;
30800     Roo.each(req, function(e) {
30801         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30802             throw "Roo.form.ComboCheck : missing value for: " + e;
30803         }
30804     });
30805     
30806     
30807 };
30808
30809 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30810      
30811      
30812     editable : false,
30813      
30814     selectedClass: 'x-menu-item-checked', 
30815     
30816     // private
30817     onRender : function(ct, position){
30818         var _t = this;
30819         
30820         
30821         
30822         if(!this.tpl){
30823             var cls = 'x-combo-list';
30824
30825             
30826             this.tpl =  new Roo.Template({
30827                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30828                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30829                    '<span>{' + this.displayField + '}</span>' +
30830                     '</div>' 
30831                 
30832             });
30833         }
30834  
30835         
30836         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30837         this.view.singleSelect = false;
30838         this.view.multiSelect = true;
30839         this.view.toggleSelect = true;
30840         this.pageTb.add(new Roo.Toolbar.Fill(), {
30841             
30842             text: 'Done',
30843             handler: function()
30844             {
30845                 _t.collapse();
30846             }
30847         });
30848     },
30849     
30850     onViewOver : function(e, t){
30851         // do nothing...
30852         return;
30853         
30854     },
30855     
30856     onViewClick : function(doFocus,index){
30857         return;
30858         
30859     },
30860     select: function () {
30861         //Roo.log("SELECT CALLED");
30862     },
30863      
30864     selectByValue : function(xv, scrollIntoView){
30865         var ar = this.getValueArray();
30866         var sels = [];
30867         
30868         Roo.each(ar, function(v) {
30869             if(v === undefined || v === null){
30870                 return;
30871             }
30872             var r = this.findRecord(this.valueField, v);
30873             if(r){
30874                 sels.push(this.store.indexOf(r))
30875                 
30876             }
30877         },this);
30878         this.view.select(sels);
30879         return false;
30880     },
30881     
30882     
30883     
30884     onSelect : function(record, index){
30885        // Roo.log("onselect Called");
30886        // this is only called by the clear button now..
30887         this.view.clearSelections();
30888         this.setValue('[]');
30889         if (this.value != this.valueBefore) {
30890             this.fireEvent('change', this, this.value, this.valueBefore);
30891             this.valueBefore = this.value;
30892         }
30893     },
30894     getValueArray : function()
30895     {
30896         var ar = [] ;
30897         
30898         try {
30899             //Roo.log(this.value);
30900             if (typeof(this.value) == 'undefined') {
30901                 return [];
30902             }
30903             var ar = Roo.decode(this.value);
30904             return  ar instanceof Array ? ar : []; //?? valid?
30905             
30906         } catch(e) {
30907             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30908             return [];
30909         }
30910          
30911     },
30912     expand : function ()
30913     {
30914         
30915         Roo.form.ComboCheck.superclass.expand.call(this);
30916         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30917         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30918         
30919
30920     },
30921     
30922     collapse : function(){
30923         Roo.form.ComboCheck.superclass.collapse.call(this);
30924         var sl = this.view.getSelectedIndexes();
30925         var st = this.store;
30926         var nv = [];
30927         var tv = [];
30928         var r;
30929         Roo.each(sl, function(i) {
30930             r = st.getAt(i);
30931             nv.push(r.get(this.valueField));
30932         },this);
30933         this.setValue(Roo.encode(nv));
30934         if (this.value != this.valueBefore) {
30935
30936             this.fireEvent('change', this, this.value, this.valueBefore);
30937             this.valueBefore = this.value;
30938         }
30939         
30940     },
30941     
30942     setValue : function(v){
30943         // Roo.log(v);
30944         this.value = v;
30945         
30946         var vals = this.getValueArray();
30947         var tv = [];
30948         Roo.each(vals, function(k) {
30949             var r = this.findRecord(this.valueField, k);
30950             if(r){
30951                 tv.push(r.data[this.displayField]);
30952             }else if(this.valueNotFoundText !== undefined){
30953                 tv.push( this.valueNotFoundText );
30954             }
30955         },this);
30956        // Roo.log(tv);
30957         
30958         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30959         this.hiddenField.value = v;
30960         this.value = v;
30961     }
30962     
30963 });/*
30964  * Based on:
30965  * Ext JS Library 1.1.1
30966  * Copyright(c) 2006-2007, Ext JS, LLC.
30967  *
30968  * Originally Released Under LGPL - original licence link has changed is not relivant.
30969  *
30970  * Fork - LGPL
30971  * <script type="text/javascript">
30972  */
30973  
30974 /**
30975  * @class Roo.form.Signature
30976  * @extends Roo.form.Field
30977  * Signature field.  
30978  * @constructor
30979  * 
30980  * @param {Object} config Configuration options
30981  */
30982
30983 Roo.form.Signature = function(config){
30984     Roo.form.Signature.superclass.constructor.call(this, config);
30985     
30986     this.addEvents({// not in used??
30987          /**
30988          * @event confirm
30989          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30990              * @param {Roo.form.Signature} combo This combo box
30991              */
30992         'confirm' : true,
30993         /**
30994          * @event reset
30995          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30996              * @param {Roo.form.ComboBox} combo This combo box
30997              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30998              */
30999         'reset' : true
31000     });
31001 };
31002
31003 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31004     /**
31005      * @cfg {Object} labels Label to use when rendering a form.
31006      * defaults to 
31007      * labels : { 
31008      *      clear : "Clear",
31009      *      confirm : "Confirm"
31010      *  }
31011      */
31012     labels : { 
31013         clear : "Clear",
31014         confirm : "Confirm"
31015     },
31016     /**
31017      * @cfg {Number} width The signature panel width (defaults to 300)
31018      */
31019     width: 300,
31020     /**
31021      * @cfg {Number} height The signature panel height (defaults to 100)
31022      */
31023     height : 100,
31024     /**
31025      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31026      */
31027     allowBlank : false,
31028     
31029     //private
31030     // {Object} signPanel The signature SVG panel element (defaults to {})
31031     signPanel : {},
31032     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31033     isMouseDown : false,
31034     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31035     isConfirmed : false,
31036     // {String} signatureTmp SVG mapping string (defaults to empty string)
31037     signatureTmp : '',
31038     
31039     
31040     defaultAutoCreate : { // modified by initCompnoent..
31041         tag: "input",
31042         type:"hidden"
31043     },
31044
31045     // private
31046     onRender : function(ct, position){
31047         
31048         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31049         
31050         this.wrap = this.el.wrap({
31051             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31052         });
31053         
31054         this.createToolbar(this);
31055         this.signPanel = this.wrap.createChild({
31056                 tag: 'div',
31057                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31058             }, this.el
31059         );
31060             
31061         this.svgID = Roo.id();
31062         this.svgEl = this.signPanel.createChild({
31063               xmlns : 'http://www.w3.org/2000/svg',
31064               tag : 'svg',
31065               id : this.svgID + "-svg",
31066               width: this.width,
31067               height: this.height,
31068               viewBox: '0 0 '+this.width+' '+this.height,
31069               cn : [
31070                 {
31071                     tag: "rect",
31072                     id: this.svgID + "-svg-r",
31073                     width: this.width,
31074                     height: this.height,
31075                     fill: "#ffa"
31076                 },
31077                 {
31078                     tag: "line",
31079                     id: this.svgID + "-svg-l",
31080                     x1: "0", // start
31081                     y1: (this.height*0.8), // start set the line in 80% of height
31082                     x2: this.width, // end
31083                     y2: (this.height*0.8), // end set the line in 80% of height
31084                     'stroke': "#666",
31085                     'stroke-width': "1",
31086                     'stroke-dasharray': "3",
31087                     'shape-rendering': "crispEdges",
31088                     'pointer-events': "none"
31089                 },
31090                 {
31091                     tag: "path",
31092                     id: this.svgID + "-svg-p",
31093                     'stroke': "navy",
31094                     'stroke-width': "3",
31095                     'fill': "none",
31096                     'pointer-events': 'none'
31097                 }
31098               ]
31099         });
31100         this.createSVG();
31101         this.svgBox = this.svgEl.dom.getScreenCTM();
31102     },
31103     createSVG : function(){ 
31104         var svg = this.signPanel;
31105         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31106         var t = this;
31107
31108         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31109         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31110         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31111         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31112         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31113         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31114         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31115         
31116     },
31117     isTouchEvent : function(e){
31118         return e.type.match(/^touch/);
31119     },
31120     getCoords : function (e) {
31121         var pt    = this.svgEl.dom.createSVGPoint();
31122         pt.x = e.clientX; 
31123         pt.y = e.clientY;
31124         if (this.isTouchEvent(e)) {
31125             pt.x =  e.targetTouches[0].clientX 
31126             pt.y = e.targetTouches[0].clientY;
31127         }
31128         var a = this.svgEl.dom.getScreenCTM();
31129         var b = a.inverse();
31130         var mx = pt.matrixTransform(b);
31131         return mx.x + ',' + mx.y;
31132     },
31133     //mouse event headler 
31134     down : function (e) {
31135         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31136         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31137         
31138         this.isMouseDown = true;
31139         
31140         e.preventDefault();
31141     },
31142     move : function (e) {
31143         if (this.isMouseDown) {
31144             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31145             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31146         }
31147         
31148         e.preventDefault();
31149     },
31150     up : function (e) {
31151         this.isMouseDown = false;
31152         var sp = this.signatureTmp.split(' ');
31153         
31154         if(sp.length > 1){
31155             if(!sp[sp.length-2].match(/^L/)){
31156                 sp.pop();
31157                 sp.pop();
31158                 sp.push("");
31159                 this.signatureTmp = sp.join(" ");
31160             }
31161         }
31162         if(this.getValue() != this.signatureTmp){
31163             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31164             this.isConfirmed = false;
31165         }
31166         e.preventDefault();
31167     },
31168     
31169     /**
31170      * Protected method that will not generally be called directly. It
31171      * is called when the editor creates its toolbar. Override this method if you need to
31172      * add custom toolbar buttons.
31173      * @param {HtmlEditor} editor
31174      */
31175     createToolbar : function(editor){
31176          function btn(id, toggle, handler){
31177             var xid = fid + '-'+ id ;
31178             return {
31179                 id : xid,
31180                 cmd : id,
31181                 cls : 'x-btn-icon x-edit-'+id,
31182                 enableToggle:toggle !== false,
31183                 scope: editor, // was editor...
31184                 handler:handler||editor.relayBtnCmd,
31185                 clickEvent:'mousedown',
31186                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31187                 tabIndex:-1
31188             };
31189         }
31190         
31191         
31192         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31193         this.tb = tb;
31194         this.tb.add(
31195            {
31196                 cls : ' x-signature-btn x-signature-'+id,
31197                 scope: editor, // was editor...
31198                 handler: this.reset,
31199                 clickEvent:'mousedown',
31200                 text: this.labels.clear
31201             },
31202             {
31203                  xtype : 'Fill',
31204                  xns: Roo.Toolbar
31205             }, 
31206             {
31207                 cls : '  x-signature-btn x-signature-'+id,
31208                 scope: editor, // was editor...
31209                 handler: this.confirmHandler,
31210                 clickEvent:'mousedown',
31211                 text: this.labels.confirm
31212             }
31213         );
31214     
31215     },
31216     //public
31217     /**
31218      * when user is clicked confirm then show this image.....
31219      * 
31220      * @return {String} Image Data URI
31221      */
31222     getImageDataURI : function(){
31223         var svg = this.svgEl.dom.parentNode.innerHTML;
31224         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31225         return src; 
31226     },
31227     /**
31228      * 
31229      * @return {Boolean} this.isConfirmed
31230      */
31231     getConfirmed : function(){
31232         return this.isConfirmed;
31233     },
31234     /**
31235      * 
31236      * @return {Number} this.width
31237      */
31238     getWidth : function(){
31239         return this.width;
31240     },
31241     /**
31242      * 
31243      * @return {Number} this.height
31244      */
31245     getHeight : function(){
31246         return this.height;
31247     },
31248     // private
31249     getSignature : function(){
31250         return this.signatureTmp;
31251     },
31252     // private
31253     reset : function(){
31254         this.signatureTmp = '';
31255         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31256         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31257         this.isConfirmed = false;
31258         Roo.form.Signature.superclass.reset.call(this);
31259     },
31260     setSignature : function(s){
31261         this.signatureTmp = s;
31262         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31263         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31264         this.setValue(s);
31265         this.isConfirmed = false;
31266         Roo.form.Signature.superclass.reset.call(this);
31267     }, 
31268     test : function(){
31269 //        Roo.log(this.signPanel.dom.contentWindow.up())
31270     },
31271     //private
31272     setConfirmed : function(){
31273         
31274         
31275         
31276 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31277     },
31278     // private
31279     confirmHandler : function(){
31280         if(!this.getSignature()){
31281             return;
31282         }
31283         
31284         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31285         this.setValue(this.getSignature());
31286         this.isConfirmed = true;
31287         
31288         this.fireEvent('confirm', this);
31289     },
31290     // private
31291     // Subclasses should provide the validation implementation by overriding this
31292     validateValue : function(value){
31293         if(this.allowBlank){
31294             return true;
31295         }
31296         
31297         if(this.isConfirmed){
31298             return true;
31299         }
31300         return false;
31301     }
31302 });/*
31303  * Based on:
31304  * Ext JS Library 1.1.1
31305  * Copyright(c) 2006-2007, Ext JS, LLC.
31306  *
31307  * Originally Released Under LGPL - original licence link has changed is not relivant.
31308  *
31309  * Fork - LGPL
31310  * <script type="text/javascript">
31311  */
31312  
31313
31314 /**
31315  * @class Roo.form.ComboBox
31316  * @extends Roo.form.TriggerField
31317  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31318  * @constructor
31319  * Create a new ComboBox.
31320  * @param {Object} config Configuration options
31321  */
31322 Roo.form.Select = function(config){
31323     Roo.form.Select.superclass.constructor.call(this, config);
31324      
31325 };
31326
31327 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31328     /**
31329      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31330      */
31331     /**
31332      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31333      * rendering into an Roo.Editor, defaults to false)
31334      */
31335     /**
31336      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31337      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31338      */
31339     /**
31340      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31341      */
31342     /**
31343      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31344      * the dropdown list (defaults to undefined, with no header element)
31345      */
31346
31347      /**
31348      * @cfg {String/Roo.Template} tpl The template to use to render the output
31349      */
31350      
31351     // private
31352     defaultAutoCreate : {tag: "select"  },
31353     /**
31354      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31355      */
31356     listWidth: undefined,
31357     /**
31358      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31359      * mode = 'remote' or 'text' if mode = 'local')
31360      */
31361     displayField: undefined,
31362     /**
31363      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31364      * mode = 'remote' or 'value' if mode = 'local'). 
31365      * Note: use of a valueField requires the user make a selection
31366      * in order for a value to be mapped.
31367      */
31368     valueField: undefined,
31369     
31370     
31371     /**
31372      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31373      * field's data value (defaults to the underlying DOM element's name)
31374      */
31375     hiddenName: undefined,
31376     /**
31377      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31378      */
31379     listClass: '',
31380     /**
31381      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31382      */
31383     selectedClass: 'x-combo-selected',
31384     /**
31385      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31386      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31387      * which displays a downward arrow icon).
31388      */
31389     triggerClass : 'x-form-arrow-trigger',
31390     /**
31391      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31392      */
31393     shadow:'sides',
31394     /**
31395      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31396      * anchor positions (defaults to 'tl-bl')
31397      */
31398     listAlign: 'tl-bl?',
31399     /**
31400      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31401      */
31402     maxHeight: 300,
31403     /**
31404      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31405      * query specified by the allQuery config option (defaults to 'query')
31406      */
31407     triggerAction: 'query',
31408     /**
31409      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31410      * (defaults to 4, does not apply if editable = false)
31411      */
31412     minChars : 4,
31413     /**
31414      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31415      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31416      */
31417     typeAhead: false,
31418     /**
31419      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31420      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31421      */
31422     queryDelay: 500,
31423     /**
31424      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31425      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31426      */
31427     pageSize: 0,
31428     /**
31429      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31430      * when editable = true (defaults to false)
31431      */
31432     selectOnFocus:false,
31433     /**
31434      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31435      */
31436     queryParam: 'query',
31437     /**
31438      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31439      * when mode = 'remote' (defaults to 'Loading...')
31440      */
31441     loadingText: 'Loading...',
31442     /**
31443      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31444      */
31445     resizable: false,
31446     /**
31447      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31448      */
31449     handleHeight : 8,
31450     /**
31451      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31452      * traditional select (defaults to true)
31453      */
31454     editable: true,
31455     /**
31456      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31457      */
31458     allQuery: '',
31459     /**
31460      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31461      */
31462     mode: 'remote',
31463     /**
31464      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31465      * listWidth has a higher value)
31466      */
31467     minListWidth : 70,
31468     /**
31469      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31470      * allow the user to set arbitrary text into the field (defaults to false)
31471      */
31472     forceSelection:false,
31473     /**
31474      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31475      * if typeAhead = true (defaults to 250)
31476      */
31477     typeAheadDelay : 250,
31478     /**
31479      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31480      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31481      */
31482     valueNotFoundText : undefined,
31483     
31484     /**
31485      * @cfg {String} defaultValue The value displayed after loading the store.
31486      */
31487     defaultValue: '',
31488     
31489     /**
31490      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31491      */
31492     blockFocus : false,
31493     
31494     /**
31495      * @cfg {Boolean} disableClear Disable showing of clear button.
31496      */
31497     disableClear : false,
31498     /**
31499      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31500      */
31501     alwaysQuery : false,
31502     
31503     //private
31504     addicon : false,
31505     editicon: false,
31506     
31507     // element that contains real text value.. (when hidden is used..)
31508      
31509     // private
31510     onRender : function(ct, position){
31511         Roo.form.Field.prototype.onRender.call(this, ct, position);
31512         
31513         if(this.store){
31514             this.store.on('beforeload', this.onBeforeLoad, this);
31515             this.store.on('load', this.onLoad, this);
31516             this.store.on('loadexception', this.onLoadException, this);
31517             this.store.load({});
31518         }
31519         
31520         
31521         
31522     },
31523
31524     // private
31525     initEvents : function(){
31526         //Roo.form.ComboBox.superclass.initEvents.call(this);
31527  
31528     },
31529
31530     onDestroy : function(){
31531        
31532         if(this.store){
31533             this.store.un('beforeload', this.onBeforeLoad, this);
31534             this.store.un('load', this.onLoad, this);
31535             this.store.un('loadexception', this.onLoadException, this);
31536         }
31537         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31538     },
31539
31540     // private
31541     fireKey : function(e){
31542         if(e.isNavKeyPress() && !this.list.isVisible()){
31543             this.fireEvent("specialkey", this, e);
31544         }
31545     },
31546
31547     // private
31548     onResize: function(w, h){
31549         
31550         return; 
31551     
31552         
31553     },
31554
31555     /**
31556      * Allow or prevent the user from directly editing the field text.  If false is passed,
31557      * the user will only be able to select from the items defined in the dropdown list.  This method
31558      * is the runtime equivalent of setting the 'editable' config option at config time.
31559      * @param {Boolean} value True to allow the user to directly edit the field text
31560      */
31561     setEditable : function(value){
31562          
31563     },
31564
31565     // private
31566     onBeforeLoad : function(){
31567         
31568         Roo.log("Select before load");
31569         return;
31570     
31571         this.innerList.update(this.loadingText ?
31572                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31573         //this.restrictHeight();
31574         this.selectedIndex = -1;
31575     },
31576
31577     // private
31578     onLoad : function(){
31579
31580     
31581         var dom = this.el.dom;
31582         dom.innerHTML = '';
31583          var od = dom.ownerDocument;
31584          
31585         if (this.emptyText) {
31586             var op = od.createElement('option');
31587             op.setAttribute('value', '');
31588             op.innerHTML = String.format('{0}', this.emptyText);
31589             dom.appendChild(op);
31590         }
31591         if(this.store.getCount() > 0){
31592            
31593             var vf = this.valueField;
31594             var df = this.displayField;
31595             this.store.data.each(function(r) {
31596                 // which colmsn to use... testing - cdoe / title..
31597                 var op = od.createElement('option');
31598                 op.setAttribute('value', r.data[vf]);
31599                 op.innerHTML = String.format('{0}', r.data[df]);
31600                 dom.appendChild(op);
31601             });
31602             if (typeof(this.defaultValue != 'undefined')) {
31603                 this.setValue(this.defaultValue);
31604             }
31605             
31606              
31607         }else{
31608             //this.onEmptyResults();
31609         }
31610         //this.el.focus();
31611     },
31612     // private
31613     onLoadException : function()
31614     {
31615         dom.innerHTML = '';
31616             
31617         Roo.log("Select on load exception");
31618         return;
31619     
31620         this.collapse();
31621         Roo.log(this.store.reader.jsonData);
31622         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31623             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31624         }
31625         
31626         
31627     },
31628     // private
31629     onTypeAhead : function(){
31630          
31631     },
31632
31633     // private
31634     onSelect : function(record, index){
31635         Roo.log('on select?');
31636         return;
31637         if(this.fireEvent('beforeselect', this, record, index) !== false){
31638             this.setFromData(index > -1 ? record.data : false);
31639             this.collapse();
31640             this.fireEvent('select', this, record, index);
31641         }
31642     },
31643
31644     /**
31645      * Returns the currently selected field value or empty string if no value is set.
31646      * @return {String} value The selected value
31647      */
31648     getValue : function(){
31649         var dom = this.el.dom;
31650         this.value = dom.options[dom.selectedIndex].value;
31651         return this.value;
31652         
31653     },
31654
31655     /**
31656      * Clears any text/value currently set in the field
31657      */
31658     clearValue : function(){
31659         this.value = '';
31660         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31661         
31662     },
31663
31664     /**
31665      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31666      * will be displayed in the field.  If the value does not match the data value of an existing item,
31667      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31668      * Otherwise the field will be blank (although the value will still be set).
31669      * @param {String} value The value to match
31670      */
31671     setValue : function(v){
31672         var d = this.el.dom;
31673         for (var i =0; i < d.options.length;i++) {
31674             if (v == d.options[i].value) {
31675                 d.selectedIndex = i;
31676                 this.value = v;
31677                 return;
31678             }
31679         }
31680         this.clearValue();
31681     },
31682     /**
31683      * @property {Object} the last set data for the element
31684      */
31685     
31686     lastData : false,
31687     /**
31688      * Sets the value of the field based on a object which is related to the record format for the store.
31689      * @param {Object} value the value to set as. or false on reset?
31690      */
31691     setFromData : function(o){
31692         Roo.log('setfrom data?');
31693          
31694         
31695         
31696     },
31697     // private
31698     reset : function(){
31699         this.clearValue();
31700     },
31701     // private
31702     findRecord : function(prop, value){
31703         
31704         return false;
31705     
31706         var record;
31707         if(this.store.getCount() > 0){
31708             this.store.each(function(r){
31709                 if(r.data[prop] == value){
31710                     record = r;
31711                     return false;
31712                 }
31713                 return true;
31714             });
31715         }
31716         return record;
31717     },
31718     
31719     getName: function()
31720     {
31721         // returns hidden if it's set..
31722         if (!this.rendered) {return ''};
31723         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31724         
31725     },
31726      
31727
31728     
31729
31730     // private
31731     onEmptyResults : function(){
31732         Roo.log('empty results');
31733         //this.collapse();
31734     },
31735
31736     /**
31737      * Returns true if the dropdown list is expanded, else false.
31738      */
31739     isExpanded : function(){
31740         return false;
31741     },
31742
31743     /**
31744      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31745      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31746      * @param {String} value The data value of the item to select
31747      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31748      * selected item if it is not currently in view (defaults to true)
31749      * @return {Boolean} True if the value matched an item in the list, else false
31750      */
31751     selectByValue : function(v, scrollIntoView){
31752         Roo.log('select By Value');
31753         return false;
31754     
31755         if(v !== undefined && v !== null){
31756             var r = this.findRecord(this.valueField || this.displayField, v);
31757             if(r){
31758                 this.select(this.store.indexOf(r), scrollIntoView);
31759                 return true;
31760             }
31761         }
31762         return false;
31763     },
31764
31765     /**
31766      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31767      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31768      * @param {Number} index The zero-based index of the list item to select
31769      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31770      * selected item if it is not currently in view (defaults to true)
31771      */
31772     select : function(index, scrollIntoView){
31773         Roo.log('select ');
31774         return  ;
31775         
31776         this.selectedIndex = index;
31777         this.view.select(index);
31778         if(scrollIntoView !== false){
31779             var el = this.view.getNode(index);
31780             if(el){
31781                 this.innerList.scrollChildIntoView(el, false);
31782             }
31783         }
31784     },
31785
31786       
31787
31788     // private
31789     validateBlur : function(){
31790         
31791         return;
31792         
31793     },
31794
31795     // private
31796     initQuery : function(){
31797         this.doQuery(this.getRawValue());
31798     },
31799
31800     // private
31801     doForce : function(){
31802         if(this.el.dom.value.length > 0){
31803             this.el.dom.value =
31804                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31805              
31806         }
31807     },
31808
31809     /**
31810      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31811      * query allowing the query action to be canceled if needed.
31812      * @param {String} query The SQL query to execute
31813      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31814      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31815      * saved in the current store (defaults to false)
31816      */
31817     doQuery : function(q, forceAll){
31818         
31819         Roo.log('doQuery?');
31820         if(q === undefined || q === null){
31821             q = '';
31822         }
31823         var qe = {
31824             query: q,
31825             forceAll: forceAll,
31826             combo: this,
31827             cancel:false
31828         };
31829         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31830             return false;
31831         }
31832         q = qe.query;
31833         forceAll = qe.forceAll;
31834         if(forceAll === true || (q.length >= this.minChars)){
31835             if(this.lastQuery != q || this.alwaysQuery){
31836                 this.lastQuery = q;
31837                 if(this.mode == 'local'){
31838                     this.selectedIndex = -1;
31839                     if(forceAll){
31840                         this.store.clearFilter();
31841                     }else{
31842                         this.store.filter(this.displayField, q);
31843                     }
31844                     this.onLoad();
31845                 }else{
31846                     this.store.baseParams[this.queryParam] = q;
31847                     this.store.load({
31848                         params: this.getParams(q)
31849                     });
31850                     this.expand();
31851                 }
31852             }else{
31853                 this.selectedIndex = -1;
31854                 this.onLoad();   
31855             }
31856         }
31857     },
31858
31859     // private
31860     getParams : function(q){
31861         var p = {};
31862         //p[this.queryParam] = q;
31863         if(this.pageSize){
31864             p.start = 0;
31865             p.limit = this.pageSize;
31866         }
31867         return p;
31868     },
31869
31870     /**
31871      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31872      */
31873     collapse : function(){
31874         
31875     },
31876
31877     // private
31878     collapseIf : function(e){
31879         
31880     },
31881
31882     /**
31883      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31884      */
31885     expand : function(){
31886         
31887     } ,
31888
31889     // private
31890      
31891
31892     /** 
31893     * @cfg {Boolean} grow 
31894     * @hide 
31895     */
31896     /** 
31897     * @cfg {Number} growMin 
31898     * @hide 
31899     */
31900     /** 
31901     * @cfg {Number} growMax 
31902     * @hide 
31903     */
31904     /**
31905      * @hide
31906      * @method autoSize
31907      */
31908     
31909     setWidth : function()
31910     {
31911         
31912     },
31913     getResizeEl : function(){
31914         return this.el;
31915     }
31916 });//<script type="text/javasscript">
31917  
31918
31919 /**
31920  * @class Roo.DDView
31921  * A DnD enabled version of Roo.View.
31922  * @param {Element/String} container The Element in which to create the View.
31923  * @param {String} tpl The template string used to create the markup for each element of the View
31924  * @param {Object} config The configuration properties. These include all the config options of
31925  * {@link Roo.View} plus some specific to this class.<br>
31926  * <p>
31927  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31928  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31929  * <p>
31930  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31931 .x-view-drag-insert-above {
31932         border-top:1px dotted #3366cc;
31933 }
31934 .x-view-drag-insert-below {
31935         border-bottom:1px dotted #3366cc;
31936 }
31937 </code></pre>
31938  * 
31939  */
31940  
31941 Roo.DDView = function(container, tpl, config) {
31942     Roo.DDView.superclass.constructor.apply(this, arguments);
31943     this.getEl().setStyle("outline", "0px none");
31944     this.getEl().unselectable();
31945     if (this.dragGroup) {
31946                 this.setDraggable(this.dragGroup.split(","));
31947     }
31948     if (this.dropGroup) {
31949                 this.setDroppable(this.dropGroup.split(","));
31950     }
31951     if (this.deletable) {
31952         this.setDeletable();
31953     }
31954     this.isDirtyFlag = false;
31955         this.addEvents({
31956                 "drop" : true
31957         });
31958 };
31959
31960 Roo.extend(Roo.DDView, Roo.View, {
31961 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31962 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31963 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31964 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31965
31966         isFormField: true,
31967
31968         reset: Roo.emptyFn,
31969         
31970         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31971
31972         validate: function() {
31973                 return true;
31974         },
31975         
31976         destroy: function() {
31977                 this.purgeListeners();
31978                 this.getEl.removeAllListeners();
31979                 this.getEl().remove();
31980                 if (this.dragZone) {
31981                         if (this.dragZone.destroy) {
31982                                 this.dragZone.destroy();
31983                         }
31984                 }
31985                 if (this.dropZone) {
31986                         if (this.dropZone.destroy) {
31987                                 this.dropZone.destroy();
31988                         }
31989                 }
31990         },
31991
31992 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31993         getName: function() {
31994                 return this.name;
31995         },
31996
31997 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31998         setValue: function(v) {
31999                 if (!this.store) {
32000                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32001                 }
32002                 var data = {};
32003                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32004                 this.store.proxy = new Roo.data.MemoryProxy(data);
32005                 this.store.load();
32006         },
32007
32008 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32009         getValue: function() {
32010                 var result = '(';
32011                 this.store.each(function(rec) {
32012                         result += rec.id + ',';
32013                 });
32014                 return result.substr(0, result.length - 1) + ')';
32015         },
32016         
32017         getIds: function() {
32018                 var i = 0, result = new Array(this.store.getCount());
32019                 this.store.each(function(rec) {
32020                         result[i++] = rec.id;
32021                 });
32022                 return result;
32023         },
32024         
32025         isDirty: function() {
32026                 return this.isDirtyFlag;
32027         },
32028
32029 /**
32030  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32031  *      whole Element becomes the target, and this causes the drop gesture to append.
32032  */
32033     getTargetFromEvent : function(e) {
32034                 var target = e.getTarget();
32035                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32036                 target = target.parentNode;
32037                 }
32038                 if (!target) {
32039                         target = this.el.dom.lastChild || this.el.dom;
32040                 }
32041                 return target;
32042     },
32043
32044 /**
32045  *      Create the drag data which consists of an object which has the property "ddel" as
32046  *      the drag proxy element. 
32047  */
32048     getDragData : function(e) {
32049         var target = this.findItemFromChild(e.getTarget());
32050                 if(target) {
32051                         this.handleSelection(e);
32052                         var selNodes = this.getSelectedNodes();
32053             var dragData = {
32054                 source: this,
32055                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32056                 nodes: selNodes,
32057                 records: []
32058                         };
32059                         var selectedIndices = this.getSelectedIndexes();
32060                         for (var i = 0; i < selectedIndices.length; i++) {
32061                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32062                         }
32063                         if (selNodes.length == 1) {
32064                                 dragData.ddel = target.cloneNode(true); // the div element
32065                         } else {
32066                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32067                                 div.className = 'multi-proxy';
32068                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32069                                         div.appendChild(selNodes[i].cloneNode(true));
32070                                 }
32071                                 dragData.ddel = div;
32072                         }
32073             //console.log(dragData)
32074             //console.log(dragData.ddel.innerHTML)
32075                         return dragData;
32076                 }
32077         //console.log('nodragData')
32078                 return false;
32079     },
32080     
32081 /**     Specify to which ddGroup items in this DDView may be dragged. */
32082     setDraggable: function(ddGroup) {
32083         if (ddGroup instanceof Array) {
32084                 Roo.each(ddGroup, this.setDraggable, this);
32085                 return;
32086         }
32087         if (this.dragZone) {
32088                 this.dragZone.addToGroup(ddGroup);
32089         } else {
32090                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32091                                 containerScroll: true,
32092                                 ddGroup: ddGroup 
32093
32094                         });
32095 //                      Draggability implies selection. DragZone's mousedown selects the element.
32096                         if (!this.multiSelect) { this.singleSelect = true; }
32097
32098 //                      Wire the DragZone's handlers up to methods in *this*
32099                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32100                 }
32101     },
32102
32103 /**     Specify from which ddGroup this DDView accepts drops. */
32104     setDroppable: function(ddGroup) {
32105         if (ddGroup instanceof Array) {
32106                 Roo.each(ddGroup, this.setDroppable, this);
32107                 return;
32108         }
32109         if (this.dropZone) {
32110                 this.dropZone.addToGroup(ddGroup);
32111         } else {
32112                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32113                                 containerScroll: true,
32114                                 ddGroup: ddGroup
32115                         });
32116
32117 //                      Wire the DropZone's handlers up to methods in *this*
32118                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32119                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32120                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32121                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32122                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32123                 }
32124     },
32125
32126 /**     Decide whether to drop above or below a View node. */
32127     getDropPoint : function(e, n, dd){
32128         if (n == this.el.dom) { return "above"; }
32129                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32130                 var c = t + (b - t) / 2;
32131                 var y = Roo.lib.Event.getPageY(e);
32132                 if(y <= c) {
32133                         return "above";
32134                 }else{
32135                         return "below";
32136                 }
32137     },
32138
32139     onNodeEnter : function(n, dd, e, data){
32140                 return false;
32141     },
32142     
32143     onNodeOver : function(n, dd, e, data){
32144                 var pt = this.getDropPoint(e, n, dd);
32145                 // set the insert point style on the target node
32146                 var dragElClass = this.dropNotAllowed;
32147                 if (pt) {
32148                         var targetElClass;
32149                         if (pt == "above"){
32150                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32151                                 targetElClass = "x-view-drag-insert-above";
32152                         } else {
32153                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32154                                 targetElClass = "x-view-drag-insert-below";
32155                         }
32156                         if (this.lastInsertClass != targetElClass){
32157                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32158                                 this.lastInsertClass = targetElClass;
32159                         }
32160                 }
32161                 return dragElClass;
32162         },
32163
32164     onNodeOut : function(n, dd, e, data){
32165                 this.removeDropIndicators(n);
32166     },
32167
32168     onNodeDrop : function(n, dd, e, data){
32169         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32170                 return false;
32171         }
32172         var pt = this.getDropPoint(e, n, dd);
32173                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32174                 if (pt == "below") { insertAt++; }
32175                 for (var i = 0; i < data.records.length; i++) {
32176                         var r = data.records[i];
32177                         var dup = this.store.getById(r.id);
32178                         if (dup && (dd != this.dragZone)) {
32179                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32180                         } else {
32181                                 if (data.copy) {
32182                                         this.store.insert(insertAt++, r.copy());
32183                                 } else {
32184                                         data.source.isDirtyFlag = true;
32185                                         r.store.remove(r);
32186                                         this.store.insert(insertAt++, r);
32187                                 }
32188                                 this.isDirtyFlag = true;
32189                         }
32190                 }
32191                 this.dragZone.cachedTarget = null;
32192                 return true;
32193     },
32194
32195     removeDropIndicators : function(n){
32196                 if(n){
32197                         Roo.fly(n).removeClass([
32198                                 "x-view-drag-insert-above",
32199                                 "x-view-drag-insert-below"]);
32200                         this.lastInsertClass = "_noclass";
32201                 }
32202     },
32203
32204 /**
32205  *      Utility method. Add a delete option to the DDView's context menu.
32206  *      @param {String} imageUrl The URL of the "delete" icon image.
32207  */
32208         setDeletable: function(imageUrl) {
32209                 if (!this.singleSelect && !this.multiSelect) {
32210                         this.singleSelect = true;
32211                 }
32212                 var c = this.getContextMenu();
32213                 this.contextMenu.on("itemclick", function(item) {
32214                         switch (item.id) {
32215                                 case "delete":
32216                                         this.remove(this.getSelectedIndexes());
32217                                         break;
32218                         }
32219                 }, this);
32220                 this.contextMenu.add({
32221                         icon: imageUrl,
32222                         id: "delete",
32223                         text: 'Delete'
32224                 });
32225         },
32226         
32227 /**     Return the context menu for this DDView. */
32228         getContextMenu: function() {
32229                 if (!this.contextMenu) {
32230 //                      Create the View's context menu
32231                         this.contextMenu = new Roo.menu.Menu({
32232                                 id: this.id + "-contextmenu"
32233                         });
32234                         this.el.on("contextmenu", this.showContextMenu, this);
32235                 }
32236                 return this.contextMenu;
32237         },
32238         
32239         disableContextMenu: function() {
32240                 if (this.contextMenu) {
32241                         this.el.un("contextmenu", this.showContextMenu, this);
32242                 }
32243         },
32244
32245         showContextMenu: function(e, item) {
32246         item = this.findItemFromChild(e.getTarget());
32247                 if (item) {
32248                         e.stopEvent();
32249                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32250                         this.contextMenu.showAt(e.getXY());
32251             }
32252     },
32253
32254 /**
32255  *      Remove {@link Roo.data.Record}s at the specified indices.
32256  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32257  */
32258     remove: function(selectedIndices) {
32259                 selectedIndices = [].concat(selectedIndices);
32260                 for (var i = 0; i < selectedIndices.length; i++) {
32261                         var rec = this.store.getAt(selectedIndices[i]);
32262                         this.store.remove(rec);
32263                 }
32264     },
32265
32266 /**
32267  *      Double click fires the event, but also, if this is draggable, and there is only one other
32268  *      related DropZone, it transfers the selected node.
32269  */
32270     onDblClick : function(e){
32271         var item = this.findItemFromChild(e.getTarget());
32272         if(item){
32273             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32274                 return false;
32275             }
32276             if (this.dragGroup) {
32277                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32278                     while (targets.indexOf(this.dropZone) > -1) {
32279                             targets.remove(this.dropZone);
32280                                 }
32281                     if (targets.length == 1) {
32282                                         this.dragZone.cachedTarget = null;
32283                         var el = Roo.get(targets[0].getEl());
32284                         var box = el.getBox(true);
32285                         targets[0].onNodeDrop(el.dom, {
32286                                 target: el.dom,
32287                                 xy: [box.x, box.y + box.height - 1]
32288                         }, null, this.getDragData(e));
32289                     }
32290                 }
32291         }
32292     },
32293     
32294     handleSelection: function(e) {
32295                 this.dragZone.cachedTarget = null;
32296         var item = this.findItemFromChild(e.getTarget());
32297         if (!item) {
32298                 this.clearSelections(true);
32299                 return;
32300         }
32301                 if (item && (this.multiSelect || this.singleSelect)){
32302                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32303                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32304                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32305                                 this.unselect(item);
32306                         } else {
32307                                 this.select(item, this.multiSelect && e.ctrlKey);
32308                                 this.lastSelection = item;
32309                         }
32310                 }
32311     },
32312
32313     onItemClick : function(item, index, e){
32314                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32315                         return false;
32316                 }
32317                 return true;
32318     },
32319
32320     unselect : function(nodeInfo, suppressEvent){
32321                 var node = this.getNode(nodeInfo);
32322                 if(node && this.isSelected(node)){
32323                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32324                                 Roo.fly(node).removeClass(this.selectedClass);
32325                                 this.selections.remove(node);
32326                                 if(!suppressEvent){
32327                                         this.fireEvent("selectionchange", this, this.selections);
32328                                 }
32329                         }
32330                 }
32331     }
32332 });
32333 /*
32334  * Based on:
32335  * Ext JS Library 1.1.1
32336  * Copyright(c) 2006-2007, Ext JS, LLC.
32337  *
32338  * Originally Released Under LGPL - original licence link has changed is not relivant.
32339  *
32340  * Fork - LGPL
32341  * <script type="text/javascript">
32342  */
32343  
32344 /**
32345  * @class Roo.LayoutManager
32346  * @extends Roo.util.Observable
32347  * Base class for layout managers.
32348  */
32349 Roo.LayoutManager = function(container, config){
32350     Roo.LayoutManager.superclass.constructor.call(this);
32351     this.el = Roo.get(container);
32352     // ie scrollbar fix
32353     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32354         document.body.scroll = "no";
32355     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32356         this.el.position('relative');
32357     }
32358     this.id = this.el.id;
32359     this.el.addClass("x-layout-container");
32360     /** false to disable window resize monitoring @type Boolean */
32361     this.monitorWindowResize = true;
32362     this.regions = {};
32363     this.addEvents({
32364         /**
32365          * @event layout
32366          * Fires when a layout is performed. 
32367          * @param {Roo.LayoutManager} this
32368          */
32369         "layout" : true,
32370         /**
32371          * @event regionresized
32372          * Fires when the user resizes a region. 
32373          * @param {Roo.LayoutRegion} region The resized region
32374          * @param {Number} newSize The new size (width for east/west, height for north/south)
32375          */
32376         "regionresized" : true,
32377         /**
32378          * @event regioncollapsed
32379          * Fires when a region is collapsed. 
32380          * @param {Roo.LayoutRegion} region The collapsed region
32381          */
32382         "regioncollapsed" : true,
32383         /**
32384          * @event regionexpanded
32385          * Fires when a region is expanded.  
32386          * @param {Roo.LayoutRegion} region The expanded region
32387          */
32388         "regionexpanded" : true
32389     });
32390     this.updating = false;
32391     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32392 };
32393
32394 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32395     /**
32396      * Returns true if this layout is currently being updated
32397      * @return {Boolean}
32398      */
32399     isUpdating : function(){
32400         return this.updating; 
32401     },
32402     
32403     /**
32404      * Suspend the LayoutManager from doing auto-layouts while
32405      * making multiple add or remove calls
32406      */
32407     beginUpdate : function(){
32408         this.updating = true;    
32409     },
32410     
32411     /**
32412      * Restore auto-layouts and optionally disable the manager from performing a layout
32413      * @param {Boolean} noLayout true to disable a layout update 
32414      */
32415     endUpdate : function(noLayout){
32416         this.updating = false;
32417         if(!noLayout){
32418             this.layout();
32419         }    
32420     },
32421     
32422     layout: function(){
32423         
32424     },
32425     
32426     onRegionResized : function(region, newSize){
32427         this.fireEvent("regionresized", region, newSize);
32428         this.layout();
32429     },
32430     
32431     onRegionCollapsed : function(region){
32432         this.fireEvent("regioncollapsed", region);
32433     },
32434     
32435     onRegionExpanded : function(region){
32436         this.fireEvent("regionexpanded", region);
32437     },
32438         
32439     /**
32440      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32441      * performs box-model adjustments.
32442      * @return {Object} The size as an object {width: (the width), height: (the height)}
32443      */
32444     getViewSize : function(){
32445         var size;
32446         if(this.el.dom != document.body){
32447             size = this.el.getSize();
32448         }else{
32449             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32450         }
32451         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32452         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32453         return size;
32454     },
32455     
32456     /**
32457      * Returns the Element this layout is bound to.
32458      * @return {Roo.Element}
32459      */
32460     getEl : function(){
32461         return this.el;
32462     },
32463     
32464     /**
32465      * Returns the specified region.
32466      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32467      * @return {Roo.LayoutRegion}
32468      */
32469     getRegion : function(target){
32470         return this.regions[target.toLowerCase()];
32471     },
32472     
32473     onWindowResize : function(){
32474         if(this.monitorWindowResize){
32475             this.layout();
32476         }
32477     }
32478 });/*
32479  * Based on:
32480  * Ext JS Library 1.1.1
32481  * Copyright(c) 2006-2007, Ext JS, LLC.
32482  *
32483  * Originally Released Under LGPL - original licence link has changed is not relivant.
32484  *
32485  * Fork - LGPL
32486  * <script type="text/javascript">
32487  */
32488 /**
32489  * @class Roo.BorderLayout
32490  * @extends Roo.LayoutManager
32491  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32492  * please see: <br><br>
32493  * <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>
32494  * <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>
32495  * Example:
32496  <pre><code>
32497  var layout = new Roo.BorderLayout(document.body, {
32498     north: {
32499         initialSize: 25,
32500         titlebar: false
32501     },
32502     west: {
32503         split:true,
32504         initialSize: 200,
32505         minSize: 175,
32506         maxSize: 400,
32507         titlebar: true,
32508         collapsible: true
32509     },
32510     east: {
32511         split:true,
32512         initialSize: 202,
32513         minSize: 175,
32514         maxSize: 400,
32515         titlebar: true,
32516         collapsible: true
32517     },
32518     south: {
32519         split:true,
32520         initialSize: 100,
32521         minSize: 100,
32522         maxSize: 200,
32523         titlebar: true,
32524         collapsible: true
32525     },
32526     center: {
32527         titlebar: true,
32528         autoScroll:true,
32529         resizeTabs: true,
32530         minTabWidth: 50,
32531         preferredTabWidth: 150
32532     }
32533 });
32534
32535 // shorthand
32536 var CP = Roo.ContentPanel;
32537
32538 layout.beginUpdate();
32539 layout.add("north", new CP("north", "North"));
32540 layout.add("south", new CP("south", {title: "South", closable: true}));
32541 layout.add("west", new CP("west", {title: "West"}));
32542 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32543 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32544 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32545 layout.getRegion("center").showPanel("center1");
32546 layout.endUpdate();
32547 </code></pre>
32548
32549 <b>The container the layout is rendered into can be either the body element or any other element.
32550 If it is not the body element, the container needs to either be an absolute positioned element,
32551 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32552 the container size if it is not the body element.</b>
32553
32554 * @constructor
32555 * Create a new BorderLayout
32556 * @param {String/HTMLElement/Element} container The container this layout is bound to
32557 * @param {Object} config Configuration options
32558  */
32559 Roo.BorderLayout = function(container, config){
32560     config = config || {};
32561     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32562     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32563     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32564         var target = this.factory.validRegions[i];
32565         if(config[target]){
32566             this.addRegion(target, config[target]);
32567         }
32568     }
32569 };
32570
32571 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32572     /**
32573      * Creates and adds a new region if it doesn't already exist.
32574      * @param {String} target The target region key (north, south, east, west or center).
32575      * @param {Object} config The regions config object
32576      * @return {BorderLayoutRegion} The new region
32577      */
32578     addRegion : function(target, config){
32579         if(!this.regions[target]){
32580             var r = this.factory.create(target, this, config);
32581             this.bindRegion(target, r);
32582         }
32583         return this.regions[target];
32584     },
32585
32586     // private (kinda)
32587     bindRegion : function(name, r){
32588         this.regions[name] = r;
32589         r.on("visibilitychange", this.layout, this);
32590         r.on("paneladded", this.layout, this);
32591         r.on("panelremoved", this.layout, this);
32592         r.on("invalidated", this.layout, this);
32593         r.on("resized", this.onRegionResized, this);
32594         r.on("collapsed", this.onRegionCollapsed, this);
32595         r.on("expanded", this.onRegionExpanded, this);
32596     },
32597
32598     /**
32599      * Performs a layout update.
32600      */
32601     layout : function(){
32602         if(this.updating) return;
32603         var size = this.getViewSize();
32604         var w = size.width;
32605         var h = size.height;
32606         var centerW = w;
32607         var centerH = h;
32608         var centerY = 0;
32609         var centerX = 0;
32610         //var x = 0, y = 0;
32611
32612         var rs = this.regions;
32613         var north = rs["north"];
32614         var south = rs["south"]; 
32615         var west = rs["west"];
32616         var east = rs["east"];
32617         var center = rs["center"];
32618         //if(this.hideOnLayout){ // not supported anymore
32619             //c.el.setStyle("display", "none");
32620         //}
32621         if(north && north.isVisible()){
32622             var b = north.getBox();
32623             var m = north.getMargins();
32624             b.width = w - (m.left+m.right);
32625             b.x = m.left;
32626             b.y = m.top;
32627             centerY = b.height + b.y + m.bottom;
32628             centerH -= centerY;
32629             north.updateBox(this.safeBox(b));
32630         }
32631         if(south && south.isVisible()){
32632             var b = south.getBox();
32633             var m = south.getMargins();
32634             b.width = w - (m.left+m.right);
32635             b.x = m.left;
32636             var totalHeight = (b.height + m.top + m.bottom);
32637             b.y = h - totalHeight + m.top;
32638             centerH -= totalHeight;
32639             south.updateBox(this.safeBox(b));
32640         }
32641         if(west && west.isVisible()){
32642             var b = west.getBox();
32643             var m = west.getMargins();
32644             b.height = centerH - (m.top+m.bottom);
32645             b.x = m.left;
32646             b.y = centerY + m.top;
32647             var totalWidth = (b.width + m.left + m.right);
32648             centerX += totalWidth;
32649             centerW -= totalWidth;
32650             west.updateBox(this.safeBox(b));
32651         }
32652         if(east && east.isVisible()){
32653             var b = east.getBox();
32654             var m = east.getMargins();
32655             b.height = centerH - (m.top+m.bottom);
32656             var totalWidth = (b.width + m.left + m.right);
32657             b.x = w - totalWidth + m.left;
32658             b.y = centerY + m.top;
32659             centerW -= totalWidth;
32660             east.updateBox(this.safeBox(b));
32661         }
32662         if(center){
32663             var m = center.getMargins();
32664             var centerBox = {
32665                 x: centerX + m.left,
32666                 y: centerY + m.top,
32667                 width: centerW - (m.left+m.right),
32668                 height: centerH - (m.top+m.bottom)
32669             };
32670             //if(this.hideOnLayout){
32671                 //center.el.setStyle("display", "block");
32672             //}
32673             center.updateBox(this.safeBox(centerBox));
32674         }
32675         this.el.repaint();
32676         this.fireEvent("layout", this);
32677     },
32678
32679     // private
32680     safeBox : function(box){
32681         box.width = Math.max(0, box.width);
32682         box.height = Math.max(0, box.height);
32683         return box;
32684     },
32685
32686     /**
32687      * Adds a ContentPanel (or subclass) to this layout.
32688      * @param {String} target The target region key (north, south, east, west or center).
32689      * @param {Roo.ContentPanel} panel The panel to add
32690      * @return {Roo.ContentPanel} The added panel
32691      */
32692     add : function(target, panel){
32693          
32694         target = target.toLowerCase();
32695         return this.regions[target].add(panel);
32696     },
32697
32698     /**
32699      * Remove a ContentPanel (or subclass) to this layout.
32700      * @param {String} target The target region key (north, south, east, west or center).
32701      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32702      * @return {Roo.ContentPanel} The removed panel
32703      */
32704     remove : function(target, panel){
32705         target = target.toLowerCase();
32706         return this.regions[target].remove(panel);
32707     },
32708
32709     /**
32710      * Searches all regions for a panel with the specified id
32711      * @param {String} panelId
32712      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32713      */
32714     findPanel : function(panelId){
32715         var rs = this.regions;
32716         for(var target in rs){
32717             if(typeof rs[target] != "function"){
32718                 var p = rs[target].getPanel(panelId);
32719                 if(p){
32720                     return p;
32721                 }
32722             }
32723         }
32724         return null;
32725     },
32726
32727     /**
32728      * Searches all regions for a panel with the specified id and activates (shows) it.
32729      * @param {String/ContentPanel} panelId The panels id or the panel itself
32730      * @return {Roo.ContentPanel} The shown panel or null
32731      */
32732     showPanel : function(panelId) {
32733       var rs = this.regions;
32734       for(var target in rs){
32735          var r = rs[target];
32736          if(typeof r != "function"){
32737             if(r.hasPanel(panelId)){
32738                return r.showPanel(panelId);
32739             }
32740          }
32741       }
32742       return null;
32743    },
32744
32745    /**
32746      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32747      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32748      */
32749     restoreState : function(provider){
32750         if(!provider){
32751             provider = Roo.state.Manager;
32752         }
32753         var sm = new Roo.LayoutStateManager();
32754         sm.init(this, provider);
32755     },
32756
32757     /**
32758      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32759      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32760      * a valid ContentPanel config object.  Example:
32761      * <pre><code>
32762 // Create the main layout
32763 var layout = new Roo.BorderLayout('main-ct', {
32764     west: {
32765         split:true,
32766         minSize: 175,
32767         titlebar: true
32768     },
32769     center: {
32770         title:'Components'
32771     }
32772 }, 'main-ct');
32773
32774 // Create and add multiple ContentPanels at once via configs
32775 layout.batchAdd({
32776    west: {
32777        id: 'source-files',
32778        autoCreate:true,
32779        title:'Ext Source Files',
32780        autoScroll:true,
32781        fitToFrame:true
32782    },
32783    center : {
32784        el: cview,
32785        autoScroll:true,
32786        fitToFrame:true,
32787        toolbar: tb,
32788        resizeEl:'cbody'
32789    }
32790 });
32791 </code></pre>
32792      * @param {Object} regions An object containing ContentPanel configs by region name
32793      */
32794     batchAdd : function(regions){
32795         this.beginUpdate();
32796         for(var rname in regions){
32797             var lr = this.regions[rname];
32798             if(lr){
32799                 this.addTypedPanels(lr, regions[rname]);
32800             }
32801         }
32802         this.endUpdate();
32803     },
32804
32805     // private
32806     addTypedPanels : function(lr, ps){
32807         if(typeof ps == 'string'){
32808             lr.add(new Roo.ContentPanel(ps));
32809         }
32810         else if(ps instanceof Array){
32811             for(var i =0, len = ps.length; i < len; i++){
32812                 this.addTypedPanels(lr, ps[i]);
32813             }
32814         }
32815         else if(!ps.events){ // raw config?
32816             var el = ps.el;
32817             delete ps.el; // prevent conflict
32818             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32819         }
32820         else {  // panel object assumed!
32821             lr.add(ps);
32822         }
32823     },
32824     /**
32825      * Adds a xtype elements to the layout.
32826      * <pre><code>
32827
32828 layout.addxtype({
32829        xtype : 'ContentPanel',
32830        region: 'west',
32831        items: [ .... ]
32832    }
32833 );
32834
32835 layout.addxtype({
32836         xtype : 'NestedLayoutPanel',
32837         region: 'west',
32838         layout: {
32839            center: { },
32840            west: { }   
32841         },
32842         items : [ ... list of content panels or nested layout panels.. ]
32843    }
32844 );
32845 </code></pre>
32846      * @param {Object} cfg Xtype definition of item to add.
32847      */
32848     addxtype : function(cfg)
32849     {
32850         // basically accepts a pannel...
32851         // can accept a layout region..!?!?
32852         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32853         
32854         if (!cfg.xtype.match(/Panel$/)) {
32855             return false;
32856         }
32857         var ret = false;
32858         
32859         if (typeof(cfg.region) == 'undefined') {
32860             Roo.log("Failed to add Panel, region was not set");
32861             Roo.log(cfg);
32862             return false;
32863         }
32864         var region = cfg.region;
32865         delete cfg.region;
32866         
32867           
32868         var xitems = [];
32869         if (cfg.items) {
32870             xitems = cfg.items;
32871             delete cfg.items;
32872         }
32873         var nb = false;
32874         
32875         switch(cfg.xtype) 
32876         {
32877             case 'ContentPanel':  // ContentPanel (el, cfg)
32878             case 'ScrollPanel':  // ContentPanel (el, cfg)
32879             case 'ViewPanel': 
32880                 if(cfg.autoCreate) {
32881                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32882                 } else {
32883                     var el = this.el.createChild();
32884                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32885                 }
32886                 
32887                 this.add(region, ret);
32888                 break;
32889             
32890             
32891             case 'TreePanel': // our new panel!
32892                 cfg.el = this.el.createChild();
32893                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32894                 this.add(region, ret);
32895                 break;
32896             
32897             case 'NestedLayoutPanel': 
32898                 // create a new Layout (which is  a Border Layout...
32899                 var el = this.el.createChild();
32900                 var clayout = cfg.layout;
32901                 delete cfg.layout;
32902                 clayout.items   = clayout.items  || [];
32903                 // replace this exitems with the clayout ones..
32904                 xitems = clayout.items;
32905                  
32906                 
32907                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32908                     cfg.background = false;
32909                 }
32910                 var layout = new Roo.BorderLayout(el, clayout);
32911                 
32912                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32913                 //console.log('adding nested layout panel '  + cfg.toSource());
32914                 this.add(region, ret);
32915                 nb = {}; /// find first...
32916                 break;
32917                 
32918             case 'GridPanel': 
32919             
32920                 // needs grid and region
32921                 
32922                 //var el = this.getRegion(region).el.createChild();
32923                 var el = this.el.createChild();
32924                 // create the grid first...
32925                 
32926                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32927                 delete cfg.grid;
32928                 if (region == 'center' && this.active ) {
32929                     cfg.background = false;
32930                 }
32931                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32932                 
32933                 this.add(region, ret);
32934                 if (cfg.background) {
32935                     ret.on('activate', function(gp) {
32936                         if (!gp.grid.rendered) {
32937                             gp.grid.render();
32938                         }
32939                     });
32940                 } else {
32941                     grid.render();
32942                 }
32943                 break;
32944            
32945            
32946            
32947                 
32948                 
32949                 
32950             default:
32951                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32952                     
32953                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32954                     this.add(region, ret);
32955                 } else {
32956                 
32957                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32958                     return null;
32959                 }
32960                 
32961              // GridPanel (grid, cfg)
32962             
32963         }
32964         this.beginUpdate();
32965         // add children..
32966         var region = '';
32967         var abn = {};
32968         Roo.each(xitems, function(i)  {
32969             region = nb && i.region ? i.region : false;
32970             
32971             var add = ret.addxtype(i);
32972            
32973             if (region) {
32974                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32975                 if (!i.background) {
32976                     abn[region] = nb[region] ;
32977                 }
32978             }
32979             
32980         });
32981         this.endUpdate();
32982
32983         // make the last non-background panel active..
32984         //if (nb) { Roo.log(abn); }
32985         if (nb) {
32986             
32987             for(var r in abn) {
32988                 region = this.getRegion(r);
32989                 if (region) {
32990                     // tried using nb[r], but it does not work..
32991                      
32992                     region.showPanel(abn[r]);
32993                    
32994                 }
32995             }
32996         }
32997         return ret;
32998         
32999     }
33000 });
33001
33002 /**
33003  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33004  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33005  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33006  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33007  * <pre><code>
33008 // shorthand
33009 var CP = Roo.ContentPanel;
33010
33011 var layout = Roo.BorderLayout.create({
33012     north: {
33013         initialSize: 25,
33014         titlebar: false,
33015         panels: [new CP("north", "North")]
33016     },
33017     west: {
33018         split:true,
33019         initialSize: 200,
33020         minSize: 175,
33021         maxSize: 400,
33022         titlebar: true,
33023         collapsible: true,
33024         panels: [new CP("west", {title: "West"})]
33025     },
33026     east: {
33027         split:true,
33028         initialSize: 202,
33029         minSize: 175,
33030         maxSize: 400,
33031         titlebar: true,
33032         collapsible: true,
33033         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33034     },
33035     south: {
33036         split:true,
33037         initialSize: 100,
33038         minSize: 100,
33039         maxSize: 200,
33040         titlebar: true,
33041         collapsible: true,
33042         panels: [new CP("south", {title: "South", closable: true})]
33043     },
33044     center: {
33045         titlebar: true,
33046         autoScroll:true,
33047         resizeTabs: true,
33048         minTabWidth: 50,
33049         preferredTabWidth: 150,
33050         panels: [
33051             new CP("center1", {title: "Close Me", closable: true}),
33052             new CP("center2", {title: "Center Panel", closable: false})
33053         ]
33054     }
33055 }, document.body);
33056
33057 layout.getRegion("center").showPanel("center1");
33058 </code></pre>
33059  * @param config
33060  * @param targetEl
33061  */
33062 Roo.BorderLayout.create = function(config, targetEl){
33063     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33064     layout.beginUpdate();
33065     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33066     for(var j = 0, jlen = regions.length; j < jlen; j++){
33067         var lr = regions[j];
33068         if(layout.regions[lr] && config[lr].panels){
33069             var r = layout.regions[lr];
33070             var ps = config[lr].panels;
33071             layout.addTypedPanels(r, ps);
33072         }
33073     }
33074     layout.endUpdate();
33075     return layout;
33076 };
33077
33078 // private
33079 Roo.BorderLayout.RegionFactory = {
33080     // private
33081     validRegions : ["north","south","east","west","center"],
33082
33083     // private
33084     create : function(target, mgr, config){
33085         target = target.toLowerCase();
33086         if(config.lightweight || config.basic){
33087             return new Roo.BasicLayoutRegion(mgr, config, target);
33088         }
33089         switch(target){
33090             case "north":
33091                 return new Roo.NorthLayoutRegion(mgr, config);
33092             case "south":
33093                 return new Roo.SouthLayoutRegion(mgr, config);
33094             case "east":
33095                 return new Roo.EastLayoutRegion(mgr, config);
33096             case "west":
33097                 return new Roo.WestLayoutRegion(mgr, config);
33098             case "center":
33099                 return new Roo.CenterLayoutRegion(mgr, config);
33100         }
33101         throw 'Layout region "'+target+'" not supported.';
33102     }
33103 };/*
33104  * Based on:
33105  * Ext JS Library 1.1.1
33106  * Copyright(c) 2006-2007, Ext JS, LLC.
33107  *
33108  * Originally Released Under LGPL - original licence link has changed is not relivant.
33109  *
33110  * Fork - LGPL
33111  * <script type="text/javascript">
33112  */
33113  
33114 /**
33115  * @class Roo.BasicLayoutRegion
33116  * @extends Roo.util.Observable
33117  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33118  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33119  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33120  */
33121 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33122     this.mgr = mgr;
33123     this.position  = pos;
33124     this.events = {
33125         /**
33126          * @scope Roo.BasicLayoutRegion
33127          */
33128         
33129         /**
33130          * @event beforeremove
33131          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33132          * @param {Roo.LayoutRegion} this
33133          * @param {Roo.ContentPanel} panel The panel
33134          * @param {Object} e The cancel event object
33135          */
33136         "beforeremove" : true,
33137         /**
33138          * @event invalidated
33139          * Fires when the layout for this region is changed.
33140          * @param {Roo.LayoutRegion} this
33141          */
33142         "invalidated" : true,
33143         /**
33144          * @event visibilitychange
33145          * Fires when this region is shown or hidden 
33146          * @param {Roo.LayoutRegion} this
33147          * @param {Boolean} visibility true or false
33148          */
33149         "visibilitychange" : true,
33150         /**
33151          * @event paneladded
33152          * Fires when a panel is added. 
33153          * @param {Roo.LayoutRegion} this
33154          * @param {Roo.ContentPanel} panel The panel
33155          */
33156         "paneladded" : true,
33157         /**
33158          * @event panelremoved
33159          * Fires when a panel is removed. 
33160          * @param {Roo.LayoutRegion} this
33161          * @param {Roo.ContentPanel} panel The panel
33162          */
33163         "panelremoved" : true,
33164         /**
33165          * @event collapsed
33166          * Fires when this region is collapsed.
33167          * @param {Roo.LayoutRegion} this
33168          */
33169         "collapsed" : true,
33170         /**
33171          * @event expanded
33172          * Fires when this region is expanded.
33173          * @param {Roo.LayoutRegion} this
33174          */
33175         "expanded" : true,
33176         /**
33177          * @event slideshow
33178          * Fires when this region is slid into view.
33179          * @param {Roo.LayoutRegion} this
33180          */
33181         "slideshow" : true,
33182         /**
33183          * @event slidehide
33184          * Fires when this region slides out of view. 
33185          * @param {Roo.LayoutRegion} this
33186          */
33187         "slidehide" : true,
33188         /**
33189          * @event panelactivated
33190          * Fires when a panel is activated. 
33191          * @param {Roo.LayoutRegion} this
33192          * @param {Roo.ContentPanel} panel The activated panel
33193          */
33194         "panelactivated" : true,
33195         /**
33196          * @event resized
33197          * Fires when the user resizes this region. 
33198          * @param {Roo.LayoutRegion} this
33199          * @param {Number} newSize The new size (width for east/west, height for north/south)
33200          */
33201         "resized" : true
33202     };
33203     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33204     this.panels = new Roo.util.MixedCollection();
33205     this.panels.getKey = this.getPanelId.createDelegate(this);
33206     this.box = null;
33207     this.activePanel = null;
33208     // ensure listeners are added...
33209     
33210     if (config.listeners || config.events) {
33211         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33212             listeners : config.listeners || {},
33213             events : config.events || {}
33214         });
33215     }
33216     
33217     if(skipConfig !== true){
33218         this.applyConfig(config);
33219     }
33220 };
33221
33222 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33223     getPanelId : function(p){
33224         return p.getId();
33225     },
33226     
33227     applyConfig : function(config){
33228         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33229         this.config = config;
33230         
33231     },
33232     
33233     /**
33234      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33235      * the width, for horizontal (north, south) the height.
33236      * @param {Number} newSize The new width or height
33237      */
33238     resizeTo : function(newSize){
33239         var el = this.el ? this.el :
33240                  (this.activePanel ? this.activePanel.getEl() : null);
33241         if(el){
33242             switch(this.position){
33243                 case "east":
33244                 case "west":
33245                     el.setWidth(newSize);
33246                     this.fireEvent("resized", this, newSize);
33247                 break;
33248                 case "north":
33249                 case "south":
33250                     el.setHeight(newSize);
33251                     this.fireEvent("resized", this, newSize);
33252                 break;                
33253             }
33254         }
33255     },
33256     
33257     getBox : function(){
33258         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33259     },
33260     
33261     getMargins : function(){
33262         return this.margins;
33263     },
33264     
33265     updateBox : function(box){
33266         this.box = box;
33267         var el = this.activePanel.getEl();
33268         el.dom.style.left = box.x + "px";
33269         el.dom.style.top = box.y + "px";
33270         this.activePanel.setSize(box.width, box.height);
33271     },
33272     
33273     /**
33274      * Returns the container element for this region.
33275      * @return {Roo.Element}
33276      */
33277     getEl : function(){
33278         return this.activePanel;
33279     },
33280     
33281     /**
33282      * Returns true if this region is currently visible.
33283      * @return {Boolean}
33284      */
33285     isVisible : function(){
33286         return this.activePanel ? true : false;
33287     },
33288     
33289     setActivePanel : function(panel){
33290         panel = this.getPanel(panel);
33291         if(this.activePanel && this.activePanel != panel){
33292             this.activePanel.setActiveState(false);
33293             this.activePanel.getEl().setLeftTop(-10000,-10000);
33294         }
33295         this.activePanel = panel;
33296         panel.setActiveState(true);
33297         if(this.box){
33298             panel.setSize(this.box.width, this.box.height);
33299         }
33300         this.fireEvent("panelactivated", this, panel);
33301         this.fireEvent("invalidated");
33302     },
33303     
33304     /**
33305      * Show the specified panel.
33306      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33307      * @return {Roo.ContentPanel} The shown panel or null
33308      */
33309     showPanel : function(panel){
33310         if(panel = this.getPanel(panel)){
33311             this.setActivePanel(panel);
33312         }
33313         return panel;
33314     },
33315     
33316     /**
33317      * Get the active panel for this region.
33318      * @return {Roo.ContentPanel} The active panel or null
33319      */
33320     getActivePanel : function(){
33321         return this.activePanel;
33322     },
33323     
33324     /**
33325      * Add the passed ContentPanel(s)
33326      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33327      * @return {Roo.ContentPanel} The panel added (if only one was added)
33328      */
33329     add : function(panel){
33330         if(arguments.length > 1){
33331             for(var i = 0, len = arguments.length; i < len; i++) {
33332                 this.add(arguments[i]);
33333             }
33334             return null;
33335         }
33336         if(this.hasPanel(panel)){
33337             this.showPanel(panel);
33338             return panel;
33339         }
33340         var el = panel.getEl();
33341         if(el.dom.parentNode != this.mgr.el.dom){
33342             this.mgr.el.dom.appendChild(el.dom);
33343         }
33344         if(panel.setRegion){
33345             panel.setRegion(this);
33346         }
33347         this.panels.add(panel);
33348         el.setStyle("position", "absolute");
33349         if(!panel.background){
33350             this.setActivePanel(panel);
33351             if(this.config.initialSize && this.panels.getCount()==1){
33352                 this.resizeTo(this.config.initialSize);
33353             }
33354         }
33355         this.fireEvent("paneladded", this, panel);
33356         return panel;
33357     },
33358     
33359     /**
33360      * Returns true if the panel is in this region.
33361      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33362      * @return {Boolean}
33363      */
33364     hasPanel : function(panel){
33365         if(typeof panel == "object"){ // must be panel obj
33366             panel = panel.getId();
33367         }
33368         return this.getPanel(panel) ? true : false;
33369     },
33370     
33371     /**
33372      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33373      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33374      * @param {Boolean} preservePanel Overrides the config preservePanel option
33375      * @return {Roo.ContentPanel} The panel that was removed
33376      */
33377     remove : function(panel, preservePanel){
33378         panel = this.getPanel(panel);
33379         if(!panel){
33380             return null;
33381         }
33382         var e = {};
33383         this.fireEvent("beforeremove", this, panel, e);
33384         if(e.cancel === true){
33385             return null;
33386         }
33387         var panelId = panel.getId();
33388         this.panels.removeKey(panelId);
33389         return panel;
33390     },
33391     
33392     /**
33393      * Returns the panel specified or null if it's not in this region.
33394      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33395      * @return {Roo.ContentPanel}
33396      */
33397     getPanel : function(id){
33398         if(typeof id == "object"){ // must be panel obj
33399             return id;
33400         }
33401         return this.panels.get(id);
33402     },
33403     
33404     /**
33405      * Returns this regions position (north/south/east/west/center).
33406      * @return {String} 
33407      */
33408     getPosition: function(){
33409         return this.position;    
33410     }
33411 });/*
33412  * Based on:
33413  * Ext JS Library 1.1.1
33414  * Copyright(c) 2006-2007, Ext JS, LLC.
33415  *
33416  * Originally Released Under LGPL - original licence link has changed is not relivant.
33417  *
33418  * Fork - LGPL
33419  * <script type="text/javascript">
33420  */
33421  
33422 /**
33423  * @class Roo.LayoutRegion
33424  * @extends Roo.BasicLayoutRegion
33425  * This class represents a region in a layout manager.
33426  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33427  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33428  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33429  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33430  * @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})
33431  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33432  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33433  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33434  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33435  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33436  * @cfg {String}    title           The title for the region (overrides panel titles)
33437  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33438  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33439  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33440  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33441  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33442  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33443  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33444  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33445  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33446  * @cfg {Boolean}   showPin         True to show a pin button
33447  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33448  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33449  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33450  * @cfg {Number}    width           For East/West panels
33451  * @cfg {Number}    height          For North/South panels
33452  * @cfg {Boolean}   split           To show the splitter
33453  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33454  */
33455 Roo.LayoutRegion = function(mgr, config, pos){
33456     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33457     var dh = Roo.DomHelper;
33458     /** This region's container element 
33459     * @type Roo.Element */
33460     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33461     /** This region's title element 
33462     * @type Roo.Element */
33463
33464     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33465         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33466         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33467     ]}, true);
33468     this.titleEl.enableDisplayMode();
33469     /** This region's title text element 
33470     * @type HTMLElement */
33471     this.titleTextEl = this.titleEl.dom.firstChild;
33472     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33473     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33474     this.closeBtn.enableDisplayMode();
33475     this.closeBtn.on("click", this.closeClicked, this);
33476     this.closeBtn.hide();
33477
33478     this.createBody(config);
33479     this.visible = true;
33480     this.collapsed = false;
33481
33482     if(config.hideWhenEmpty){
33483         this.hide();
33484         this.on("paneladded", this.validateVisibility, this);
33485         this.on("panelremoved", this.validateVisibility, this);
33486     }
33487     this.applyConfig(config);
33488 };
33489
33490 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33491
33492     createBody : function(){
33493         /** This region's body element 
33494         * @type Roo.Element */
33495         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33496     },
33497
33498     applyConfig : function(c){
33499         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33500             var dh = Roo.DomHelper;
33501             if(c.titlebar !== false){
33502                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33503                 this.collapseBtn.on("click", this.collapse, this);
33504                 this.collapseBtn.enableDisplayMode();
33505
33506                 if(c.showPin === true || this.showPin){
33507                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33508                     this.stickBtn.enableDisplayMode();
33509                     this.stickBtn.on("click", this.expand, this);
33510                     this.stickBtn.hide();
33511                 }
33512             }
33513             /** This region's collapsed element
33514             * @type Roo.Element */
33515             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33516                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33517             ]}, true);
33518             if(c.floatable !== false){
33519                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33520                this.collapsedEl.on("click", this.collapseClick, this);
33521             }
33522
33523             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33524                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33525                    id: "message", unselectable: "on", style:{"float":"left"}});
33526                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33527              }
33528             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33529             this.expandBtn.on("click", this.expand, this);
33530         }
33531         if(this.collapseBtn){
33532             this.collapseBtn.setVisible(c.collapsible == true);
33533         }
33534         this.cmargins = c.cmargins || this.cmargins ||
33535                          (this.position == "west" || this.position == "east" ?
33536                              {top: 0, left: 2, right:2, bottom: 0} :
33537                              {top: 2, left: 0, right:0, bottom: 2});
33538         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33539         this.bottomTabs = c.tabPosition != "top";
33540         this.autoScroll = c.autoScroll || false;
33541         if(this.autoScroll){
33542             this.bodyEl.setStyle("overflow", "auto");
33543         }else{
33544             this.bodyEl.setStyle("overflow", "hidden");
33545         }
33546         //if(c.titlebar !== false){
33547             if((!c.titlebar && !c.title) || c.titlebar === false){
33548                 this.titleEl.hide();
33549             }else{
33550                 this.titleEl.show();
33551                 if(c.title){
33552                     this.titleTextEl.innerHTML = c.title;
33553                 }
33554             }
33555         //}
33556         this.duration = c.duration || .30;
33557         this.slideDuration = c.slideDuration || .45;
33558         this.config = c;
33559         if(c.collapsed){
33560             this.collapse(true);
33561         }
33562         if(c.hidden){
33563             this.hide();
33564         }
33565     },
33566     /**
33567      * Returns true if this region is currently visible.
33568      * @return {Boolean}
33569      */
33570     isVisible : function(){
33571         return this.visible;
33572     },
33573
33574     /**
33575      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33576      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33577      */
33578     setCollapsedTitle : function(title){
33579         title = title || "&#160;";
33580         if(this.collapsedTitleTextEl){
33581             this.collapsedTitleTextEl.innerHTML = title;
33582         }
33583     },
33584
33585     getBox : function(){
33586         var b;
33587         if(!this.collapsed){
33588             b = this.el.getBox(false, true);
33589         }else{
33590             b = this.collapsedEl.getBox(false, true);
33591         }
33592         return b;
33593     },
33594
33595     getMargins : function(){
33596         return this.collapsed ? this.cmargins : this.margins;
33597     },
33598
33599     highlight : function(){
33600         this.el.addClass("x-layout-panel-dragover");
33601     },
33602
33603     unhighlight : function(){
33604         this.el.removeClass("x-layout-panel-dragover");
33605     },
33606
33607     updateBox : function(box){
33608         this.box = box;
33609         if(!this.collapsed){
33610             this.el.dom.style.left = box.x + "px";
33611             this.el.dom.style.top = box.y + "px";
33612             this.updateBody(box.width, box.height);
33613         }else{
33614             this.collapsedEl.dom.style.left = box.x + "px";
33615             this.collapsedEl.dom.style.top = box.y + "px";
33616             this.collapsedEl.setSize(box.width, box.height);
33617         }
33618         if(this.tabs){
33619             this.tabs.autoSizeTabs();
33620         }
33621     },
33622
33623     updateBody : function(w, h){
33624         if(w !== null){
33625             this.el.setWidth(w);
33626             w -= this.el.getBorderWidth("rl");
33627             if(this.config.adjustments){
33628                 w += this.config.adjustments[0];
33629             }
33630         }
33631         if(h !== null){
33632             this.el.setHeight(h);
33633             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33634             h -= this.el.getBorderWidth("tb");
33635             if(this.config.adjustments){
33636                 h += this.config.adjustments[1];
33637             }
33638             this.bodyEl.setHeight(h);
33639             if(this.tabs){
33640                 h = this.tabs.syncHeight(h);
33641             }
33642         }
33643         if(this.panelSize){
33644             w = w !== null ? w : this.panelSize.width;
33645             h = h !== null ? h : this.panelSize.height;
33646         }
33647         if(this.activePanel){
33648             var el = this.activePanel.getEl();
33649             w = w !== null ? w : el.getWidth();
33650             h = h !== null ? h : el.getHeight();
33651             this.panelSize = {width: w, height: h};
33652             this.activePanel.setSize(w, h);
33653         }
33654         if(Roo.isIE && this.tabs){
33655             this.tabs.el.repaint();
33656         }
33657     },
33658
33659     /**
33660      * Returns the container element for this region.
33661      * @return {Roo.Element}
33662      */
33663     getEl : function(){
33664         return this.el;
33665     },
33666
33667     /**
33668      * Hides this region.
33669      */
33670     hide : function(){
33671         if(!this.collapsed){
33672             this.el.dom.style.left = "-2000px";
33673             this.el.hide();
33674         }else{
33675             this.collapsedEl.dom.style.left = "-2000px";
33676             this.collapsedEl.hide();
33677         }
33678         this.visible = false;
33679         this.fireEvent("visibilitychange", this, false);
33680     },
33681
33682     /**
33683      * Shows this region if it was previously hidden.
33684      */
33685     show : function(){
33686         if(!this.collapsed){
33687             this.el.show();
33688         }else{
33689             this.collapsedEl.show();
33690         }
33691         this.visible = true;
33692         this.fireEvent("visibilitychange", this, true);
33693     },
33694
33695     closeClicked : function(){
33696         if(this.activePanel){
33697             this.remove(this.activePanel);
33698         }
33699     },
33700
33701     collapseClick : function(e){
33702         if(this.isSlid){
33703            e.stopPropagation();
33704            this.slideIn();
33705         }else{
33706            e.stopPropagation();
33707            this.slideOut();
33708         }
33709     },
33710
33711     /**
33712      * Collapses this region.
33713      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33714      */
33715     collapse : function(skipAnim){
33716         if(this.collapsed) return;
33717         this.collapsed = true;
33718         if(this.split){
33719             this.split.el.hide();
33720         }
33721         if(this.config.animate && skipAnim !== true){
33722             this.fireEvent("invalidated", this);
33723             this.animateCollapse();
33724         }else{
33725             this.el.setLocation(-20000,-20000);
33726             this.el.hide();
33727             this.collapsedEl.show();
33728             this.fireEvent("collapsed", this);
33729             this.fireEvent("invalidated", this);
33730         }
33731     },
33732
33733     animateCollapse : function(){
33734         // overridden
33735     },
33736
33737     /**
33738      * Expands this region if it was previously collapsed.
33739      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33740      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33741      */
33742     expand : function(e, skipAnim){
33743         if(e) e.stopPropagation();
33744         if(!this.collapsed || this.el.hasActiveFx()) return;
33745         if(this.isSlid){
33746             this.afterSlideIn();
33747             skipAnim = true;
33748         }
33749         this.collapsed = false;
33750         if(this.config.animate && skipAnim !== true){
33751             this.animateExpand();
33752         }else{
33753             this.el.show();
33754             if(this.split){
33755                 this.split.el.show();
33756             }
33757             this.collapsedEl.setLocation(-2000,-2000);
33758             this.collapsedEl.hide();
33759             this.fireEvent("invalidated", this);
33760             this.fireEvent("expanded", this);
33761         }
33762     },
33763
33764     animateExpand : function(){
33765         // overridden
33766     },
33767
33768     initTabs : function()
33769     {
33770         this.bodyEl.setStyle("overflow", "hidden");
33771         var ts = new Roo.TabPanel(
33772                 this.bodyEl.dom,
33773                 {
33774                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33775                     disableTooltips: this.config.disableTabTips,
33776                     toolbar : this.config.toolbar
33777                 }
33778         );
33779         if(this.config.hideTabs){
33780             ts.stripWrap.setDisplayed(false);
33781         }
33782         this.tabs = ts;
33783         ts.resizeTabs = this.config.resizeTabs === true;
33784         ts.minTabWidth = this.config.minTabWidth || 40;
33785         ts.maxTabWidth = this.config.maxTabWidth || 250;
33786         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33787         ts.monitorResize = false;
33788         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33789         ts.bodyEl.addClass('x-layout-tabs-body');
33790         this.panels.each(this.initPanelAsTab, this);
33791     },
33792
33793     initPanelAsTab : function(panel){
33794         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33795                     this.config.closeOnTab && panel.isClosable());
33796         if(panel.tabTip !== undefined){
33797             ti.setTooltip(panel.tabTip);
33798         }
33799         ti.on("activate", function(){
33800               this.setActivePanel(panel);
33801         }, this);
33802         if(this.config.closeOnTab){
33803             ti.on("beforeclose", function(t, e){
33804                 e.cancel = true;
33805                 this.remove(panel);
33806             }, this);
33807         }
33808         return ti;
33809     },
33810
33811     updatePanelTitle : function(panel, title){
33812         if(this.activePanel == panel){
33813             this.updateTitle(title);
33814         }
33815         if(this.tabs){
33816             var ti = this.tabs.getTab(panel.getEl().id);
33817             ti.setText(title);
33818             if(panel.tabTip !== undefined){
33819                 ti.setTooltip(panel.tabTip);
33820             }
33821         }
33822     },
33823
33824     updateTitle : function(title){
33825         if(this.titleTextEl && !this.config.title){
33826             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33827         }
33828     },
33829
33830     setActivePanel : function(panel){
33831         panel = this.getPanel(panel);
33832         if(this.activePanel && this.activePanel != panel){
33833             this.activePanel.setActiveState(false);
33834         }
33835         this.activePanel = panel;
33836         panel.setActiveState(true);
33837         if(this.panelSize){
33838             panel.setSize(this.panelSize.width, this.panelSize.height);
33839         }
33840         if(this.closeBtn){
33841             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33842         }
33843         this.updateTitle(panel.getTitle());
33844         if(this.tabs){
33845             this.fireEvent("invalidated", this);
33846         }
33847         this.fireEvent("panelactivated", this, panel);
33848     },
33849
33850     /**
33851      * Shows the specified panel.
33852      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33853      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33854      */
33855     showPanel : function(panel){
33856         if(panel = this.getPanel(panel)){
33857             if(this.tabs){
33858                 var tab = this.tabs.getTab(panel.getEl().id);
33859                 if(tab.isHidden()){
33860                     this.tabs.unhideTab(tab.id);
33861                 }
33862                 tab.activate();
33863             }else{
33864                 this.setActivePanel(panel);
33865             }
33866         }
33867         return panel;
33868     },
33869
33870     /**
33871      * Get the active panel for this region.
33872      * @return {Roo.ContentPanel} The active panel or null
33873      */
33874     getActivePanel : function(){
33875         return this.activePanel;
33876     },
33877
33878     validateVisibility : function(){
33879         if(this.panels.getCount() < 1){
33880             this.updateTitle("&#160;");
33881             this.closeBtn.hide();
33882             this.hide();
33883         }else{
33884             if(!this.isVisible()){
33885                 this.show();
33886             }
33887         }
33888     },
33889
33890     /**
33891      * Adds the passed ContentPanel(s) to this region.
33892      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33893      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33894      */
33895     add : function(panel){
33896         if(arguments.length > 1){
33897             for(var i = 0, len = arguments.length; i < len; i++) {
33898                 this.add(arguments[i]);
33899             }
33900             return null;
33901         }
33902         if(this.hasPanel(panel)){
33903             this.showPanel(panel);
33904             return panel;
33905         }
33906         panel.setRegion(this);
33907         this.panels.add(panel);
33908         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33909             this.bodyEl.dom.appendChild(panel.getEl().dom);
33910             if(panel.background !== true){
33911                 this.setActivePanel(panel);
33912             }
33913             this.fireEvent("paneladded", this, panel);
33914             return panel;
33915         }
33916         if(!this.tabs){
33917             this.initTabs();
33918         }else{
33919             this.initPanelAsTab(panel);
33920         }
33921         if(panel.background !== true){
33922             this.tabs.activate(panel.getEl().id);
33923         }
33924         this.fireEvent("paneladded", this, panel);
33925         return panel;
33926     },
33927
33928     /**
33929      * Hides the tab for the specified panel.
33930      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33931      */
33932     hidePanel : function(panel){
33933         if(this.tabs && (panel = this.getPanel(panel))){
33934             this.tabs.hideTab(panel.getEl().id);
33935         }
33936     },
33937
33938     /**
33939      * Unhides the tab for a previously hidden panel.
33940      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33941      */
33942     unhidePanel : function(panel){
33943         if(this.tabs && (panel = this.getPanel(panel))){
33944             this.tabs.unhideTab(panel.getEl().id);
33945         }
33946     },
33947
33948     clearPanels : function(){
33949         while(this.panels.getCount() > 0){
33950              this.remove(this.panels.first());
33951         }
33952     },
33953
33954     /**
33955      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33956      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33957      * @param {Boolean} preservePanel Overrides the config preservePanel option
33958      * @return {Roo.ContentPanel} The panel that was removed
33959      */
33960     remove : function(panel, preservePanel){
33961         panel = this.getPanel(panel);
33962         if(!panel){
33963             return null;
33964         }
33965         var e = {};
33966         this.fireEvent("beforeremove", this, panel, e);
33967         if(e.cancel === true){
33968             return null;
33969         }
33970         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33971         var panelId = panel.getId();
33972         this.panels.removeKey(panelId);
33973         if(preservePanel){
33974             document.body.appendChild(panel.getEl().dom);
33975         }
33976         if(this.tabs){
33977             this.tabs.removeTab(panel.getEl().id);
33978         }else if (!preservePanel){
33979             this.bodyEl.dom.removeChild(panel.getEl().dom);
33980         }
33981         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33982             var p = this.panels.first();
33983             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33984             tempEl.appendChild(p.getEl().dom);
33985             this.bodyEl.update("");
33986             this.bodyEl.dom.appendChild(p.getEl().dom);
33987             tempEl = null;
33988             this.updateTitle(p.getTitle());
33989             this.tabs = null;
33990             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33991             this.setActivePanel(p);
33992         }
33993         panel.setRegion(null);
33994         if(this.activePanel == panel){
33995             this.activePanel = null;
33996         }
33997         if(this.config.autoDestroy !== false && preservePanel !== true){
33998             try{panel.destroy();}catch(e){}
33999         }
34000         this.fireEvent("panelremoved", this, panel);
34001         return panel;
34002     },
34003
34004     /**
34005      * Returns the TabPanel component used by this region
34006      * @return {Roo.TabPanel}
34007      */
34008     getTabs : function(){
34009         return this.tabs;
34010     },
34011
34012     createTool : function(parentEl, className){
34013         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34014             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34015         btn.addClassOnOver("x-layout-tools-button-over");
34016         return btn;
34017     }
34018 });/*
34019  * Based on:
34020  * Ext JS Library 1.1.1
34021  * Copyright(c) 2006-2007, Ext JS, LLC.
34022  *
34023  * Originally Released Under LGPL - original licence link has changed is not relivant.
34024  *
34025  * Fork - LGPL
34026  * <script type="text/javascript">
34027  */
34028  
34029
34030
34031 /**
34032  * @class Roo.SplitLayoutRegion
34033  * @extends Roo.LayoutRegion
34034  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34035  */
34036 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34037     this.cursor = cursor;
34038     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34039 };
34040
34041 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34042     splitTip : "Drag to resize.",
34043     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34044     useSplitTips : false,
34045
34046     applyConfig : function(config){
34047         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34048         if(config.split){
34049             if(!this.split){
34050                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34051                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34052                 /** The SplitBar for this region 
34053                 * @type Roo.SplitBar */
34054                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34055                 this.split.on("moved", this.onSplitMove, this);
34056                 this.split.useShim = config.useShim === true;
34057                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34058                 if(this.useSplitTips){
34059                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34060                 }
34061                 if(config.collapsible){
34062                     this.split.el.on("dblclick", this.collapse,  this);
34063                 }
34064             }
34065             if(typeof config.minSize != "undefined"){
34066                 this.split.minSize = config.minSize;
34067             }
34068             if(typeof config.maxSize != "undefined"){
34069                 this.split.maxSize = config.maxSize;
34070             }
34071             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34072                 this.hideSplitter();
34073             }
34074         }
34075     },
34076
34077     getHMaxSize : function(){
34078          var cmax = this.config.maxSize || 10000;
34079          var center = this.mgr.getRegion("center");
34080          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34081     },
34082
34083     getVMaxSize : function(){
34084          var cmax = this.config.maxSize || 10000;
34085          var center = this.mgr.getRegion("center");
34086          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34087     },
34088
34089     onSplitMove : function(split, newSize){
34090         this.fireEvent("resized", this, newSize);
34091     },
34092     
34093     /** 
34094      * Returns the {@link Roo.SplitBar} for this region.
34095      * @return {Roo.SplitBar}
34096      */
34097     getSplitBar : function(){
34098         return this.split;
34099     },
34100     
34101     hide : function(){
34102         this.hideSplitter();
34103         Roo.SplitLayoutRegion.superclass.hide.call(this);
34104     },
34105
34106     hideSplitter : function(){
34107         if(this.split){
34108             this.split.el.setLocation(-2000,-2000);
34109             this.split.el.hide();
34110         }
34111     },
34112
34113     show : function(){
34114         if(this.split){
34115             this.split.el.show();
34116         }
34117         Roo.SplitLayoutRegion.superclass.show.call(this);
34118     },
34119     
34120     beforeSlide: function(){
34121         if(Roo.isGecko){// firefox overflow auto bug workaround
34122             this.bodyEl.clip();
34123             if(this.tabs) this.tabs.bodyEl.clip();
34124             if(this.activePanel){
34125                 this.activePanel.getEl().clip();
34126                 
34127                 if(this.activePanel.beforeSlide){
34128                     this.activePanel.beforeSlide();
34129                 }
34130             }
34131         }
34132     },
34133     
34134     afterSlide : function(){
34135         if(Roo.isGecko){// firefox overflow auto bug workaround
34136             this.bodyEl.unclip();
34137             if(this.tabs) this.tabs.bodyEl.unclip();
34138             if(this.activePanel){
34139                 this.activePanel.getEl().unclip();
34140                 if(this.activePanel.afterSlide){
34141                     this.activePanel.afterSlide();
34142                 }
34143             }
34144         }
34145     },
34146
34147     initAutoHide : function(){
34148         if(this.autoHide !== false){
34149             if(!this.autoHideHd){
34150                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34151                 this.autoHideHd = {
34152                     "mouseout": function(e){
34153                         if(!e.within(this.el, true)){
34154                             st.delay(500);
34155                         }
34156                     },
34157                     "mouseover" : function(e){
34158                         st.cancel();
34159                     },
34160                     scope : this
34161                 };
34162             }
34163             this.el.on(this.autoHideHd);
34164         }
34165     },
34166
34167     clearAutoHide : function(){
34168         if(this.autoHide !== false){
34169             this.el.un("mouseout", this.autoHideHd.mouseout);
34170             this.el.un("mouseover", this.autoHideHd.mouseover);
34171         }
34172     },
34173
34174     clearMonitor : function(){
34175         Roo.get(document).un("click", this.slideInIf, this);
34176     },
34177
34178     // these names are backwards but not changed for compat
34179     slideOut : function(){
34180         if(this.isSlid || this.el.hasActiveFx()){
34181             return;
34182         }
34183         this.isSlid = true;
34184         if(this.collapseBtn){
34185             this.collapseBtn.hide();
34186         }
34187         this.closeBtnState = this.closeBtn.getStyle('display');
34188         this.closeBtn.hide();
34189         if(this.stickBtn){
34190             this.stickBtn.show();
34191         }
34192         this.el.show();
34193         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34194         this.beforeSlide();
34195         this.el.setStyle("z-index", 10001);
34196         this.el.slideIn(this.getSlideAnchor(), {
34197             callback: function(){
34198                 this.afterSlide();
34199                 this.initAutoHide();
34200                 Roo.get(document).on("click", this.slideInIf, this);
34201                 this.fireEvent("slideshow", this);
34202             },
34203             scope: this,
34204             block: true
34205         });
34206     },
34207
34208     afterSlideIn : function(){
34209         this.clearAutoHide();
34210         this.isSlid = false;
34211         this.clearMonitor();
34212         this.el.setStyle("z-index", "");
34213         if(this.collapseBtn){
34214             this.collapseBtn.show();
34215         }
34216         this.closeBtn.setStyle('display', this.closeBtnState);
34217         if(this.stickBtn){
34218             this.stickBtn.hide();
34219         }
34220         this.fireEvent("slidehide", this);
34221     },
34222
34223     slideIn : function(cb){
34224         if(!this.isSlid || this.el.hasActiveFx()){
34225             Roo.callback(cb);
34226             return;
34227         }
34228         this.isSlid = false;
34229         this.beforeSlide();
34230         this.el.slideOut(this.getSlideAnchor(), {
34231             callback: function(){
34232                 this.el.setLeftTop(-10000, -10000);
34233                 this.afterSlide();
34234                 this.afterSlideIn();
34235                 Roo.callback(cb);
34236             },
34237             scope: this,
34238             block: true
34239         });
34240     },
34241     
34242     slideInIf : function(e){
34243         if(!e.within(this.el)){
34244             this.slideIn();
34245         }
34246     },
34247
34248     animateCollapse : function(){
34249         this.beforeSlide();
34250         this.el.setStyle("z-index", 20000);
34251         var anchor = this.getSlideAnchor();
34252         this.el.slideOut(anchor, {
34253             callback : function(){
34254                 this.el.setStyle("z-index", "");
34255                 this.collapsedEl.slideIn(anchor, {duration:.3});
34256                 this.afterSlide();
34257                 this.el.setLocation(-10000,-10000);
34258                 this.el.hide();
34259                 this.fireEvent("collapsed", this);
34260             },
34261             scope: this,
34262             block: true
34263         });
34264     },
34265
34266     animateExpand : function(){
34267         this.beforeSlide();
34268         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34269         this.el.setStyle("z-index", 20000);
34270         this.collapsedEl.hide({
34271             duration:.1
34272         });
34273         this.el.slideIn(this.getSlideAnchor(), {
34274             callback : function(){
34275                 this.el.setStyle("z-index", "");
34276                 this.afterSlide();
34277                 if(this.split){
34278                     this.split.el.show();
34279                 }
34280                 this.fireEvent("invalidated", this);
34281                 this.fireEvent("expanded", this);
34282             },
34283             scope: this,
34284             block: true
34285         });
34286     },
34287
34288     anchors : {
34289         "west" : "left",
34290         "east" : "right",
34291         "north" : "top",
34292         "south" : "bottom"
34293     },
34294
34295     sanchors : {
34296         "west" : "l",
34297         "east" : "r",
34298         "north" : "t",
34299         "south" : "b"
34300     },
34301
34302     canchors : {
34303         "west" : "tl-tr",
34304         "east" : "tr-tl",
34305         "north" : "tl-bl",
34306         "south" : "bl-tl"
34307     },
34308
34309     getAnchor : function(){
34310         return this.anchors[this.position];
34311     },
34312
34313     getCollapseAnchor : function(){
34314         return this.canchors[this.position];
34315     },
34316
34317     getSlideAnchor : function(){
34318         return this.sanchors[this.position];
34319     },
34320
34321     getAlignAdj : function(){
34322         var cm = this.cmargins;
34323         switch(this.position){
34324             case "west":
34325                 return [0, 0];
34326             break;
34327             case "east":
34328                 return [0, 0];
34329             break;
34330             case "north":
34331                 return [0, 0];
34332             break;
34333             case "south":
34334                 return [0, 0];
34335             break;
34336         }
34337     },
34338
34339     getExpandAdj : function(){
34340         var c = this.collapsedEl, cm = this.cmargins;
34341         switch(this.position){
34342             case "west":
34343                 return [-(cm.right+c.getWidth()+cm.left), 0];
34344             break;
34345             case "east":
34346                 return [cm.right+c.getWidth()+cm.left, 0];
34347             break;
34348             case "north":
34349                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34350             break;
34351             case "south":
34352                 return [0, cm.top+cm.bottom+c.getHeight()];
34353             break;
34354         }
34355     }
34356 });/*
34357  * Based on:
34358  * Ext JS Library 1.1.1
34359  * Copyright(c) 2006-2007, Ext JS, LLC.
34360  *
34361  * Originally Released Under LGPL - original licence link has changed is not relivant.
34362  *
34363  * Fork - LGPL
34364  * <script type="text/javascript">
34365  */
34366 /*
34367  * These classes are private internal classes
34368  */
34369 Roo.CenterLayoutRegion = function(mgr, config){
34370     Roo.LayoutRegion.call(this, mgr, config, "center");
34371     this.visible = true;
34372     this.minWidth = config.minWidth || 20;
34373     this.minHeight = config.minHeight || 20;
34374 };
34375
34376 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34377     hide : function(){
34378         // center panel can't be hidden
34379     },
34380     
34381     show : function(){
34382         // center panel can't be hidden
34383     },
34384     
34385     getMinWidth: function(){
34386         return this.minWidth;
34387     },
34388     
34389     getMinHeight: function(){
34390         return this.minHeight;
34391     }
34392 });
34393
34394
34395 Roo.NorthLayoutRegion = function(mgr, config){
34396     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34397     if(this.split){
34398         this.split.placement = Roo.SplitBar.TOP;
34399         this.split.orientation = Roo.SplitBar.VERTICAL;
34400         this.split.el.addClass("x-layout-split-v");
34401     }
34402     var size = config.initialSize || config.height;
34403     if(typeof size != "undefined"){
34404         this.el.setHeight(size);
34405     }
34406 };
34407 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34408     orientation: Roo.SplitBar.VERTICAL,
34409     getBox : function(){
34410         if(this.collapsed){
34411             return this.collapsedEl.getBox();
34412         }
34413         var box = this.el.getBox();
34414         if(this.split){
34415             box.height += this.split.el.getHeight();
34416         }
34417         return box;
34418     },
34419     
34420     updateBox : function(box){
34421         if(this.split && !this.collapsed){
34422             box.height -= this.split.el.getHeight();
34423             this.split.el.setLeft(box.x);
34424             this.split.el.setTop(box.y+box.height);
34425             this.split.el.setWidth(box.width);
34426         }
34427         if(this.collapsed){
34428             this.updateBody(box.width, null);
34429         }
34430         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34431     }
34432 });
34433
34434 Roo.SouthLayoutRegion = function(mgr, config){
34435     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34436     if(this.split){
34437         this.split.placement = Roo.SplitBar.BOTTOM;
34438         this.split.orientation = Roo.SplitBar.VERTICAL;
34439         this.split.el.addClass("x-layout-split-v");
34440     }
34441     var size = config.initialSize || config.height;
34442     if(typeof size != "undefined"){
34443         this.el.setHeight(size);
34444     }
34445 };
34446 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34447     orientation: Roo.SplitBar.VERTICAL,
34448     getBox : function(){
34449         if(this.collapsed){
34450             return this.collapsedEl.getBox();
34451         }
34452         var box = this.el.getBox();
34453         if(this.split){
34454             var sh = this.split.el.getHeight();
34455             box.height += sh;
34456             box.y -= sh;
34457         }
34458         return box;
34459     },
34460     
34461     updateBox : function(box){
34462         if(this.split && !this.collapsed){
34463             var sh = this.split.el.getHeight();
34464             box.height -= sh;
34465             box.y += sh;
34466             this.split.el.setLeft(box.x);
34467             this.split.el.setTop(box.y-sh);
34468             this.split.el.setWidth(box.width);
34469         }
34470         if(this.collapsed){
34471             this.updateBody(box.width, null);
34472         }
34473         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34474     }
34475 });
34476
34477 Roo.EastLayoutRegion = function(mgr, config){
34478     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34479     if(this.split){
34480         this.split.placement = Roo.SplitBar.RIGHT;
34481         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34482         this.split.el.addClass("x-layout-split-h");
34483     }
34484     var size = config.initialSize || config.width;
34485     if(typeof size != "undefined"){
34486         this.el.setWidth(size);
34487     }
34488 };
34489 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34490     orientation: Roo.SplitBar.HORIZONTAL,
34491     getBox : function(){
34492         if(this.collapsed){
34493             return this.collapsedEl.getBox();
34494         }
34495         var box = this.el.getBox();
34496         if(this.split){
34497             var sw = this.split.el.getWidth();
34498             box.width += sw;
34499             box.x -= sw;
34500         }
34501         return box;
34502     },
34503
34504     updateBox : function(box){
34505         if(this.split && !this.collapsed){
34506             var sw = this.split.el.getWidth();
34507             box.width -= sw;
34508             this.split.el.setLeft(box.x);
34509             this.split.el.setTop(box.y);
34510             this.split.el.setHeight(box.height);
34511             box.x += sw;
34512         }
34513         if(this.collapsed){
34514             this.updateBody(null, box.height);
34515         }
34516         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34517     }
34518 });
34519
34520 Roo.WestLayoutRegion = function(mgr, config){
34521     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34522     if(this.split){
34523         this.split.placement = Roo.SplitBar.LEFT;
34524         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34525         this.split.el.addClass("x-layout-split-h");
34526     }
34527     var size = config.initialSize || config.width;
34528     if(typeof size != "undefined"){
34529         this.el.setWidth(size);
34530     }
34531 };
34532 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34533     orientation: Roo.SplitBar.HORIZONTAL,
34534     getBox : function(){
34535         if(this.collapsed){
34536             return this.collapsedEl.getBox();
34537         }
34538         var box = this.el.getBox();
34539         if(this.split){
34540             box.width += this.split.el.getWidth();
34541         }
34542         return box;
34543     },
34544     
34545     updateBox : function(box){
34546         if(this.split && !this.collapsed){
34547             var sw = this.split.el.getWidth();
34548             box.width -= sw;
34549             this.split.el.setLeft(box.x+box.width);
34550             this.split.el.setTop(box.y);
34551             this.split.el.setHeight(box.height);
34552         }
34553         if(this.collapsed){
34554             this.updateBody(null, box.height);
34555         }
34556         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34557     }
34558 });
34559 /*
34560  * Based on:
34561  * Ext JS Library 1.1.1
34562  * Copyright(c) 2006-2007, Ext JS, LLC.
34563  *
34564  * Originally Released Under LGPL - original licence link has changed is not relivant.
34565  *
34566  * Fork - LGPL
34567  * <script type="text/javascript">
34568  */
34569  
34570  
34571 /*
34572  * Private internal class for reading and applying state
34573  */
34574 Roo.LayoutStateManager = function(layout){
34575      // default empty state
34576      this.state = {
34577         north: {},
34578         south: {},
34579         east: {},
34580         west: {}       
34581     };
34582 };
34583
34584 Roo.LayoutStateManager.prototype = {
34585     init : function(layout, provider){
34586         this.provider = provider;
34587         var state = provider.get(layout.id+"-layout-state");
34588         if(state){
34589             var wasUpdating = layout.isUpdating();
34590             if(!wasUpdating){
34591                 layout.beginUpdate();
34592             }
34593             for(var key in state){
34594                 if(typeof state[key] != "function"){
34595                     var rstate = state[key];
34596                     var r = layout.getRegion(key);
34597                     if(r && rstate){
34598                         if(rstate.size){
34599                             r.resizeTo(rstate.size);
34600                         }
34601                         if(rstate.collapsed == true){
34602                             r.collapse(true);
34603                         }else{
34604                             r.expand(null, true);
34605                         }
34606                     }
34607                 }
34608             }
34609             if(!wasUpdating){
34610                 layout.endUpdate();
34611             }
34612             this.state = state; 
34613         }
34614         this.layout = layout;
34615         layout.on("regionresized", this.onRegionResized, this);
34616         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34617         layout.on("regionexpanded", this.onRegionExpanded, this);
34618     },
34619     
34620     storeState : function(){
34621         this.provider.set(this.layout.id+"-layout-state", this.state);
34622     },
34623     
34624     onRegionResized : function(region, newSize){
34625         this.state[region.getPosition()].size = newSize;
34626         this.storeState();
34627     },
34628     
34629     onRegionCollapsed : function(region){
34630         this.state[region.getPosition()].collapsed = true;
34631         this.storeState();
34632     },
34633     
34634     onRegionExpanded : function(region){
34635         this.state[region.getPosition()].collapsed = false;
34636         this.storeState();
34637     }
34638 };/*
34639  * Based on:
34640  * Ext JS Library 1.1.1
34641  * Copyright(c) 2006-2007, Ext JS, LLC.
34642  *
34643  * Originally Released Under LGPL - original licence link has changed is not relivant.
34644  *
34645  * Fork - LGPL
34646  * <script type="text/javascript">
34647  */
34648 /**
34649  * @class Roo.ContentPanel
34650  * @extends Roo.util.Observable
34651  * A basic ContentPanel element.
34652  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34653  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34654  * @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
34655  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34656  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34657  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34658  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34659  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34660  * @cfg {String} title          The title for this panel
34661  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34662  * @cfg {String} url            Calls {@link #setUrl} with this value
34663  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34664  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34665  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34666  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34667
34668  * @constructor
34669  * Create a new ContentPanel.
34670  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34671  * @param {String/Object} config A string to set only the title or a config object
34672  * @param {String} content (optional) Set the HTML content for this panel
34673  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34674  */
34675 Roo.ContentPanel = function(el, config, content){
34676     
34677      
34678     /*
34679     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34680         config = el;
34681         el = Roo.id();
34682     }
34683     if (config && config.parentLayout) { 
34684         el = config.parentLayout.el.createChild(); 
34685     }
34686     */
34687     if(el.autoCreate){ // xtype is available if this is called from factory
34688         config = el;
34689         el = Roo.id();
34690     }
34691     this.el = Roo.get(el);
34692     if(!this.el && config && config.autoCreate){
34693         if(typeof config.autoCreate == "object"){
34694             if(!config.autoCreate.id){
34695                 config.autoCreate.id = config.id||el;
34696             }
34697             this.el = Roo.DomHelper.append(document.body,
34698                         config.autoCreate, true);
34699         }else{
34700             this.el = Roo.DomHelper.append(document.body,
34701                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34702         }
34703     }
34704     this.closable = false;
34705     this.loaded = false;
34706     this.active = false;
34707     if(typeof config == "string"){
34708         this.title = config;
34709     }else{
34710         Roo.apply(this, config);
34711     }
34712     
34713     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34714         this.wrapEl = this.el.wrap();
34715         this.toolbar.container = this.el.insertSibling(false, 'before');
34716         this.toolbar = new Roo.Toolbar(this.toolbar);
34717     }
34718     
34719     // xtype created footer. - not sure if will work as we normally have to render first..
34720     if (this.footer && !this.footer.el && this.footer.xtype) {
34721         if (!this.wrapEl) {
34722             this.wrapEl = this.el.wrap();
34723         }
34724     
34725         this.footer.container = this.wrapEl.createChild();
34726          
34727         this.footer = Roo.factory(this.footer, Roo);
34728         
34729     }
34730     
34731     if(this.resizeEl){
34732         this.resizeEl = Roo.get(this.resizeEl, true);
34733     }else{
34734         this.resizeEl = this.el;
34735     }
34736     // handle view.xtype
34737     
34738  
34739     
34740     
34741     this.addEvents({
34742         /**
34743          * @event activate
34744          * Fires when this panel is activated. 
34745          * @param {Roo.ContentPanel} this
34746          */
34747         "activate" : true,
34748         /**
34749          * @event deactivate
34750          * Fires when this panel is activated. 
34751          * @param {Roo.ContentPanel} this
34752          */
34753         "deactivate" : true,
34754
34755         /**
34756          * @event resize
34757          * Fires when this panel is resized if fitToFrame is true.
34758          * @param {Roo.ContentPanel} this
34759          * @param {Number} width The width after any component adjustments
34760          * @param {Number} height The height after any component adjustments
34761          */
34762         "resize" : true,
34763         
34764          /**
34765          * @event render
34766          * Fires when this tab is created
34767          * @param {Roo.ContentPanel} this
34768          */
34769         "render" : true
34770         
34771         
34772         
34773     });
34774     
34775
34776     
34777     
34778     if(this.autoScroll){
34779         this.resizeEl.setStyle("overflow", "auto");
34780     } else {
34781         // fix randome scrolling
34782         this.el.on('scroll', function() {
34783             Roo.log('fix random scolling');
34784             this.scrollTo('top',0); 
34785         });
34786     }
34787     content = content || this.content;
34788     if(content){
34789         this.setContent(content);
34790     }
34791     if(config && config.url){
34792         this.setUrl(this.url, this.params, this.loadOnce);
34793     }
34794     
34795     
34796     
34797     Roo.ContentPanel.superclass.constructor.call(this);
34798     
34799     if (this.view && typeof(this.view.xtype) != 'undefined') {
34800         this.view.el = this.el.appendChild(document.createElement("div"));
34801         this.view = Roo.factory(this.view); 
34802         this.view.render  &&  this.view.render(false, '');  
34803     }
34804     
34805     
34806     this.fireEvent('render', this);
34807 };
34808
34809 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34810     tabTip:'',
34811     setRegion : function(region){
34812         this.region = region;
34813         if(region){
34814            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34815         }else{
34816            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34817         } 
34818     },
34819     
34820     /**
34821      * Returns the toolbar for this Panel if one was configured. 
34822      * @return {Roo.Toolbar} 
34823      */
34824     getToolbar : function(){
34825         return this.toolbar;
34826     },
34827     
34828     setActiveState : function(active){
34829         this.active = active;
34830         if(!active){
34831             this.fireEvent("deactivate", this);
34832         }else{
34833             this.fireEvent("activate", this);
34834         }
34835     },
34836     /**
34837      * Updates this panel's element
34838      * @param {String} content The new content
34839      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34840     */
34841     setContent : function(content, loadScripts){
34842         this.el.update(content, loadScripts);
34843     },
34844
34845     ignoreResize : function(w, h){
34846         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34847             return true;
34848         }else{
34849             this.lastSize = {width: w, height: h};
34850             return false;
34851         }
34852     },
34853     /**
34854      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34855      * @return {Roo.UpdateManager} The UpdateManager
34856      */
34857     getUpdateManager : function(){
34858         return this.el.getUpdateManager();
34859     },
34860      /**
34861      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34862      * @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:
34863 <pre><code>
34864 panel.load({
34865     url: "your-url.php",
34866     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34867     callback: yourFunction,
34868     scope: yourObject, //(optional scope)
34869     discardUrl: false,
34870     nocache: false,
34871     text: "Loading...",
34872     timeout: 30,
34873     scripts: false
34874 });
34875 </code></pre>
34876      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34877      * 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.
34878      * @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}
34879      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34880      * @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.
34881      * @return {Roo.ContentPanel} this
34882      */
34883     load : function(){
34884         var um = this.el.getUpdateManager();
34885         um.update.apply(um, arguments);
34886         return this;
34887     },
34888
34889
34890     /**
34891      * 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.
34892      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34893      * @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)
34894      * @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)
34895      * @return {Roo.UpdateManager} The UpdateManager
34896      */
34897     setUrl : function(url, params, loadOnce){
34898         if(this.refreshDelegate){
34899             this.removeListener("activate", this.refreshDelegate);
34900         }
34901         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34902         this.on("activate", this.refreshDelegate);
34903         return this.el.getUpdateManager();
34904     },
34905     
34906     _handleRefresh : function(url, params, loadOnce){
34907         if(!loadOnce || !this.loaded){
34908             var updater = this.el.getUpdateManager();
34909             updater.update(url, params, this._setLoaded.createDelegate(this));
34910         }
34911     },
34912     
34913     _setLoaded : function(){
34914         this.loaded = true;
34915     }, 
34916     
34917     /**
34918      * Returns this panel's id
34919      * @return {String} 
34920      */
34921     getId : function(){
34922         return this.el.id;
34923     },
34924     
34925     /** 
34926      * Returns this panel's element - used by regiosn to add.
34927      * @return {Roo.Element} 
34928      */
34929     getEl : function(){
34930         return this.wrapEl || this.el;
34931     },
34932     
34933     adjustForComponents : function(width, height)
34934     {
34935         //Roo.log('adjustForComponents ');
34936         if(this.resizeEl != this.el){
34937             width -= this.el.getFrameWidth('lr');
34938             height -= this.el.getFrameWidth('tb');
34939         }
34940         if(this.toolbar){
34941             var te = this.toolbar.getEl();
34942             height -= te.getHeight();
34943             te.setWidth(width);
34944         }
34945         if(this.footer){
34946             var te = this.footer.getEl();
34947             Roo.log("footer:" + te.getHeight());
34948             
34949             height -= te.getHeight();
34950             te.setWidth(width);
34951         }
34952         
34953         
34954         if(this.adjustments){
34955             width += this.adjustments[0];
34956             height += this.adjustments[1];
34957         }
34958         return {"width": width, "height": height};
34959     },
34960     
34961     setSize : function(width, height){
34962         if(this.fitToFrame && !this.ignoreResize(width, height)){
34963             if(this.fitContainer && this.resizeEl != this.el){
34964                 this.el.setSize(width, height);
34965             }
34966             var size = this.adjustForComponents(width, height);
34967             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34968             this.fireEvent('resize', this, size.width, size.height);
34969         }
34970     },
34971     
34972     /**
34973      * Returns this panel's title
34974      * @return {String} 
34975      */
34976     getTitle : function(){
34977         return this.title;
34978     },
34979     
34980     /**
34981      * Set this panel's title
34982      * @param {String} title
34983      */
34984     setTitle : function(title){
34985         this.title = title;
34986         if(this.region){
34987             this.region.updatePanelTitle(this, title);
34988         }
34989     },
34990     
34991     /**
34992      * Returns true is this panel was configured to be closable
34993      * @return {Boolean} 
34994      */
34995     isClosable : function(){
34996         return this.closable;
34997     },
34998     
34999     beforeSlide : function(){
35000         this.el.clip();
35001         this.resizeEl.clip();
35002     },
35003     
35004     afterSlide : function(){
35005         this.el.unclip();
35006         this.resizeEl.unclip();
35007     },
35008     
35009     /**
35010      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35011      *   Will fail silently if the {@link #setUrl} method has not been called.
35012      *   This does not activate the panel, just updates its content.
35013      */
35014     refresh : function(){
35015         if(this.refreshDelegate){
35016            this.loaded = false;
35017            this.refreshDelegate();
35018         }
35019     },
35020     
35021     /**
35022      * Destroys this panel
35023      */
35024     destroy : function(){
35025         this.el.removeAllListeners();
35026         var tempEl = document.createElement("span");
35027         tempEl.appendChild(this.el.dom);
35028         tempEl.innerHTML = "";
35029         this.el.remove();
35030         this.el = null;
35031     },
35032     
35033     /**
35034      * form - if the content panel contains a form - this is a reference to it.
35035      * @type {Roo.form.Form}
35036      */
35037     form : false,
35038     /**
35039      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35040      *    This contains a reference to it.
35041      * @type {Roo.View}
35042      */
35043     view : false,
35044     
35045       /**
35046      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35047      * <pre><code>
35048
35049 layout.addxtype({
35050        xtype : 'Form',
35051        items: [ .... ]
35052    }
35053 );
35054
35055 </code></pre>
35056      * @param {Object} cfg Xtype definition of item to add.
35057      */
35058     
35059     addxtype : function(cfg) {
35060         // add form..
35061         if (cfg.xtype.match(/^Form$/)) {
35062             
35063             var el;
35064             //if (this.footer) {
35065             //    el = this.footer.container.insertSibling(false, 'before');
35066             //} else {
35067                 el = this.el.createChild();
35068             //}
35069
35070             this.form = new  Roo.form.Form(cfg);
35071             
35072             
35073             if ( this.form.allItems.length) this.form.render(el.dom);
35074             return this.form;
35075         }
35076         // should only have one of theses..
35077         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35078             // views.. should not be just added - used named prop 'view''
35079             
35080             cfg.el = this.el.appendChild(document.createElement("div"));
35081             // factory?
35082             
35083             var ret = new Roo.factory(cfg);
35084              
35085              ret.render && ret.render(false, ''); // render blank..
35086             this.view = ret;
35087             return ret;
35088         }
35089         return false;
35090     }
35091 });
35092
35093 /**
35094  * @class Roo.GridPanel
35095  * @extends Roo.ContentPanel
35096  * @constructor
35097  * Create a new GridPanel.
35098  * @param {Roo.grid.Grid} grid The grid for this panel
35099  * @param {String/Object} config A string to set only the panel's title, or a config object
35100  */
35101 Roo.GridPanel = function(grid, config){
35102     
35103   
35104     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35105         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35106         
35107     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35108     
35109     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35110     
35111     if(this.toolbar){
35112         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35113     }
35114     // xtype created footer. - not sure if will work as we normally have to render first..
35115     if (this.footer && !this.footer.el && this.footer.xtype) {
35116         
35117         this.footer.container = this.grid.getView().getFooterPanel(true);
35118         this.footer.dataSource = this.grid.dataSource;
35119         this.footer = Roo.factory(this.footer, Roo);
35120         
35121     }
35122     
35123     grid.monitorWindowResize = false; // turn off autosizing
35124     grid.autoHeight = false;
35125     grid.autoWidth = false;
35126     this.grid = grid;
35127     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35128 };
35129
35130 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35131     getId : function(){
35132         return this.grid.id;
35133     },
35134     
35135     /**
35136      * Returns the grid for this panel
35137      * @return {Roo.grid.Grid} 
35138      */
35139     getGrid : function(){
35140         return this.grid;    
35141     },
35142     
35143     setSize : function(width, height){
35144         if(!this.ignoreResize(width, height)){
35145             var grid = this.grid;
35146             var size = this.adjustForComponents(width, height);
35147             grid.getGridEl().setSize(size.width, size.height);
35148             grid.autoSize();
35149         }
35150     },
35151     
35152     beforeSlide : function(){
35153         this.grid.getView().scroller.clip();
35154     },
35155     
35156     afterSlide : function(){
35157         this.grid.getView().scroller.unclip();
35158     },
35159     
35160     destroy : function(){
35161         this.grid.destroy();
35162         delete this.grid;
35163         Roo.GridPanel.superclass.destroy.call(this); 
35164     }
35165 });
35166
35167
35168 /**
35169  * @class Roo.NestedLayoutPanel
35170  * @extends Roo.ContentPanel
35171  * @constructor
35172  * Create a new NestedLayoutPanel.
35173  * 
35174  * 
35175  * @param {Roo.BorderLayout} layout The layout for this panel
35176  * @param {String/Object} config A string to set only the title or a config object
35177  */
35178 Roo.NestedLayoutPanel = function(layout, config)
35179 {
35180     // construct with only one argument..
35181     /* FIXME - implement nicer consturctors
35182     if (layout.layout) {
35183         config = layout;
35184         layout = config.layout;
35185         delete config.layout;
35186     }
35187     if (layout.xtype && !layout.getEl) {
35188         // then layout needs constructing..
35189         layout = Roo.factory(layout, Roo);
35190     }
35191     */
35192     
35193     
35194     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35195     
35196     layout.monitorWindowResize = false; // turn off autosizing
35197     this.layout = layout;
35198     this.layout.getEl().addClass("x-layout-nested-layout");
35199     
35200     
35201     
35202     
35203 };
35204
35205 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35206
35207     setSize : function(width, height){
35208         if(!this.ignoreResize(width, height)){
35209             var size = this.adjustForComponents(width, height);
35210             var el = this.layout.getEl();
35211             el.setSize(size.width, size.height);
35212             var touch = el.dom.offsetWidth;
35213             this.layout.layout();
35214             // ie requires a double layout on the first pass
35215             if(Roo.isIE && !this.initialized){
35216                 this.initialized = true;
35217                 this.layout.layout();
35218             }
35219         }
35220     },
35221     
35222     // activate all subpanels if not currently active..
35223     
35224     setActiveState : function(active){
35225         this.active = active;
35226         if(!active){
35227             this.fireEvent("deactivate", this);
35228             return;
35229         }
35230         
35231         this.fireEvent("activate", this);
35232         // not sure if this should happen before or after..
35233         if (!this.layout) {
35234             return; // should not happen..
35235         }
35236         var reg = false;
35237         for (var r in this.layout.regions) {
35238             reg = this.layout.getRegion(r);
35239             if (reg.getActivePanel()) {
35240                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35241                 reg.setActivePanel(reg.getActivePanel());
35242                 continue;
35243             }
35244             if (!reg.panels.length) {
35245                 continue;
35246             }
35247             reg.showPanel(reg.getPanel(0));
35248         }
35249         
35250         
35251         
35252         
35253     },
35254     
35255     /**
35256      * Returns the nested BorderLayout for this panel
35257      * @return {Roo.BorderLayout} 
35258      */
35259     getLayout : function(){
35260         return this.layout;
35261     },
35262     
35263      /**
35264      * Adds a xtype elements to the layout of the nested panel
35265      * <pre><code>
35266
35267 panel.addxtype({
35268        xtype : 'ContentPanel',
35269        region: 'west',
35270        items: [ .... ]
35271    }
35272 );
35273
35274 panel.addxtype({
35275         xtype : 'NestedLayoutPanel',
35276         region: 'west',
35277         layout: {
35278            center: { },
35279            west: { }   
35280         },
35281         items : [ ... list of content panels or nested layout panels.. ]
35282    }
35283 );
35284 </code></pre>
35285      * @param {Object} cfg Xtype definition of item to add.
35286      */
35287     addxtype : function(cfg) {
35288         return this.layout.addxtype(cfg);
35289     
35290     }
35291 });
35292
35293 Roo.ScrollPanel = function(el, config, content){
35294     config = config || {};
35295     config.fitToFrame = true;
35296     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35297     
35298     this.el.dom.style.overflow = "hidden";
35299     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35300     this.el.removeClass("x-layout-inactive-content");
35301     this.el.on("mousewheel", this.onWheel, this);
35302
35303     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35304     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35305     up.unselectable(); down.unselectable();
35306     up.on("click", this.scrollUp, this);
35307     down.on("click", this.scrollDown, this);
35308     up.addClassOnOver("x-scroller-btn-over");
35309     down.addClassOnOver("x-scroller-btn-over");
35310     up.addClassOnClick("x-scroller-btn-click");
35311     down.addClassOnClick("x-scroller-btn-click");
35312     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35313
35314     this.resizeEl = this.el;
35315     this.el = wrap; this.up = up; this.down = down;
35316 };
35317
35318 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35319     increment : 100,
35320     wheelIncrement : 5,
35321     scrollUp : function(){
35322         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35323     },
35324
35325     scrollDown : function(){
35326         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35327     },
35328
35329     afterScroll : function(){
35330         var el = this.resizeEl;
35331         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35332         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35333         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35334     },
35335
35336     setSize : function(){
35337         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35338         this.afterScroll();
35339     },
35340
35341     onWheel : function(e){
35342         var d = e.getWheelDelta();
35343         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35344         this.afterScroll();
35345         e.stopEvent();
35346     },
35347
35348     setContent : function(content, loadScripts){
35349         this.resizeEl.update(content, loadScripts);
35350     }
35351
35352 });
35353
35354
35355
35356
35357
35358
35359
35360
35361
35362 /**
35363  * @class Roo.TreePanel
35364  * @extends Roo.ContentPanel
35365  * @constructor
35366  * Create a new TreePanel. - defaults to fit/scoll contents.
35367  * @param {String/Object} config A string to set only the panel's title, or a config object
35368  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35369  */
35370 Roo.TreePanel = function(config){
35371     var el = config.el;
35372     var tree = config.tree;
35373     delete config.tree; 
35374     delete config.el; // hopefull!
35375     
35376     // wrapper for IE7 strict & safari scroll issue
35377     
35378     var treeEl = el.createChild();
35379     config.resizeEl = treeEl;
35380     
35381     
35382     
35383     Roo.TreePanel.superclass.constructor.call(this, el, config);
35384  
35385  
35386     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35387     //console.log(tree);
35388     this.on('activate', function()
35389     {
35390         if (this.tree.rendered) {
35391             return;
35392         }
35393         //console.log('render tree');
35394         this.tree.render();
35395     });
35396     // this should not be needed.. - it's actually the 'el' that resizes?
35397     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35398     
35399     //this.on('resize',  function (cp, w, h) {
35400     //        this.tree.innerCt.setWidth(w);
35401     //        this.tree.innerCt.setHeight(h);
35402     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35403     //});
35404
35405         
35406     
35407 };
35408
35409 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35410     fitToFrame : true,
35411     autoScroll : true
35412 });
35413
35414
35415
35416
35417
35418
35419
35420
35421
35422
35423
35424 /*
35425  * Based on:
35426  * Ext JS Library 1.1.1
35427  * Copyright(c) 2006-2007, Ext JS, LLC.
35428  *
35429  * Originally Released Under LGPL - original licence link has changed is not relivant.
35430  *
35431  * Fork - LGPL
35432  * <script type="text/javascript">
35433  */
35434  
35435
35436 /**
35437  * @class Roo.ReaderLayout
35438  * @extends Roo.BorderLayout
35439  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35440  * center region containing two nested regions (a top one for a list view and one for item preview below),
35441  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35442  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35443  * expedites the setup of the overall layout and regions for this common application style.
35444  * Example:
35445  <pre><code>
35446 var reader = new Roo.ReaderLayout();
35447 var CP = Roo.ContentPanel;  // shortcut for adding
35448
35449 reader.beginUpdate();
35450 reader.add("north", new CP("north", "North"));
35451 reader.add("west", new CP("west", {title: "West"}));
35452 reader.add("east", new CP("east", {title: "East"}));
35453
35454 reader.regions.listView.add(new CP("listView", "List"));
35455 reader.regions.preview.add(new CP("preview", "Preview"));
35456 reader.endUpdate();
35457 </code></pre>
35458 * @constructor
35459 * Create a new ReaderLayout
35460 * @param {Object} config Configuration options
35461 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35462 * document.body if omitted)
35463 */
35464 Roo.ReaderLayout = function(config, renderTo){
35465     var c = config || {size:{}};
35466     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35467         north: c.north !== false ? Roo.apply({
35468             split:false,
35469             initialSize: 32,
35470             titlebar: false
35471         }, c.north) : false,
35472         west: c.west !== false ? Roo.apply({
35473             split:true,
35474             initialSize: 200,
35475             minSize: 175,
35476             maxSize: 400,
35477             titlebar: true,
35478             collapsible: true,
35479             animate: true,
35480             margins:{left:5,right:0,bottom:5,top:5},
35481             cmargins:{left:5,right:5,bottom:5,top:5}
35482         }, c.west) : false,
35483         east: c.east !== false ? Roo.apply({
35484             split:true,
35485             initialSize: 200,
35486             minSize: 175,
35487             maxSize: 400,
35488             titlebar: true,
35489             collapsible: true,
35490             animate: true,
35491             margins:{left:0,right:5,bottom:5,top:5},
35492             cmargins:{left:5,right:5,bottom:5,top:5}
35493         }, c.east) : false,
35494         center: Roo.apply({
35495             tabPosition: 'top',
35496             autoScroll:false,
35497             closeOnTab: true,
35498             titlebar:false,
35499             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35500         }, c.center)
35501     });
35502
35503     this.el.addClass('x-reader');
35504
35505     this.beginUpdate();
35506
35507     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35508         south: c.preview !== false ? Roo.apply({
35509             split:true,
35510             initialSize: 200,
35511             minSize: 100,
35512             autoScroll:true,
35513             collapsible:true,
35514             titlebar: true,
35515             cmargins:{top:5,left:0, right:0, bottom:0}
35516         }, c.preview) : false,
35517         center: Roo.apply({
35518             autoScroll:false,
35519             titlebar:false,
35520             minHeight:200
35521         }, c.listView)
35522     });
35523     this.add('center', new Roo.NestedLayoutPanel(inner,
35524             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35525
35526     this.endUpdate();
35527
35528     this.regions.preview = inner.getRegion('south');
35529     this.regions.listView = inner.getRegion('center');
35530 };
35531
35532 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35533  * Based on:
35534  * Ext JS Library 1.1.1
35535  * Copyright(c) 2006-2007, Ext JS, LLC.
35536  *
35537  * Originally Released Under LGPL - original licence link has changed is not relivant.
35538  *
35539  * Fork - LGPL
35540  * <script type="text/javascript">
35541  */
35542  
35543 /**
35544  * @class Roo.grid.Grid
35545  * @extends Roo.util.Observable
35546  * This class represents the primary interface of a component based grid control.
35547  * <br><br>Usage:<pre><code>
35548  var grid = new Roo.grid.Grid("my-container-id", {
35549      ds: myDataStore,
35550      cm: myColModel,
35551      selModel: mySelectionModel,
35552      autoSizeColumns: true,
35553      monitorWindowResize: false,
35554      trackMouseOver: true
35555  });
35556  // set any options
35557  grid.render();
35558  * </code></pre>
35559  * <b>Common Problems:</b><br/>
35560  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35561  * element will correct this<br/>
35562  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35563  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35564  * are unpredictable.<br/>
35565  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35566  * grid to calculate dimensions/offsets.<br/>
35567   * @constructor
35568  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35569  * The container MUST have some type of size defined for the grid to fill. The container will be
35570  * automatically set to position relative if it isn't already.
35571  * @param {Object} config A config object that sets properties on this grid.
35572  */
35573 Roo.grid.Grid = function(container, config){
35574         // initialize the container
35575         this.container = Roo.get(container);
35576         this.container.update("");
35577         this.container.setStyle("overflow", "hidden");
35578     this.container.addClass('x-grid-container');
35579
35580     this.id = this.container.id;
35581
35582     Roo.apply(this, config);
35583     // check and correct shorthanded configs
35584     if(this.ds){
35585         this.dataSource = this.ds;
35586         delete this.ds;
35587     }
35588     if(this.cm){
35589         this.colModel = this.cm;
35590         delete this.cm;
35591     }
35592     if(this.sm){
35593         this.selModel = this.sm;
35594         delete this.sm;
35595     }
35596
35597     if (this.selModel) {
35598         this.selModel = Roo.factory(this.selModel, Roo.grid);
35599         this.sm = this.selModel;
35600         this.sm.xmodule = this.xmodule || false;
35601     }
35602     if (typeof(this.colModel.config) == 'undefined') {
35603         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35604         this.cm = this.colModel;
35605         this.cm.xmodule = this.xmodule || false;
35606     }
35607     if (this.dataSource) {
35608         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35609         this.ds = this.dataSource;
35610         this.ds.xmodule = this.xmodule || false;
35611          
35612     }
35613     
35614     
35615     
35616     if(this.width){
35617         this.container.setWidth(this.width);
35618     }
35619
35620     if(this.height){
35621         this.container.setHeight(this.height);
35622     }
35623     /** @private */
35624         this.addEvents({
35625         // raw events
35626         /**
35627          * @event click
35628          * The raw click event for the entire grid.
35629          * @param {Roo.EventObject} e
35630          */
35631         "click" : true,
35632         /**
35633          * @event dblclick
35634          * The raw dblclick event for the entire grid.
35635          * @param {Roo.EventObject} e
35636          */
35637         "dblclick" : true,
35638         /**
35639          * @event contextmenu
35640          * The raw contextmenu event for the entire grid.
35641          * @param {Roo.EventObject} e
35642          */
35643         "contextmenu" : true,
35644         /**
35645          * @event mousedown
35646          * The raw mousedown event for the entire grid.
35647          * @param {Roo.EventObject} e
35648          */
35649         "mousedown" : true,
35650         /**
35651          * @event mouseup
35652          * The raw mouseup event for the entire grid.
35653          * @param {Roo.EventObject} e
35654          */
35655         "mouseup" : true,
35656         /**
35657          * @event mouseover
35658          * The raw mouseover event for the entire grid.
35659          * @param {Roo.EventObject} e
35660          */
35661         "mouseover" : true,
35662         /**
35663          * @event mouseout
35664          * The raw mouseout event for the entire grid.
35665          * @param {Roo.EventObject} e
35666          */
35667         "mouseout" : true,
35668         /**
35669          * @event keypress
35670          * The raw keypress event for the entire grid.
35671          * @param {Roo.EventObject} e
35672          */
35673         "keypress" : true,
35674         /**
35675          * @event keydown
35676          * The raw keydown event for the entire grid.
35677          * @param {Roo.EventObject} e
35678          */
35679         "keydown" : true,
35680
35681         // custom events
35682
35683         /**
35684          * @event cellclick
35685          * Fires when a cell is clicked
35686          * @param {Grid} this
35687          * @param {Number} rowIndex
35688          * @param {Number} columnIndex
35689          * @param {Roo.EventObject} e
35690          */
35691         "cellclick" : true,
35692         /**
35693          * @event celldblclick
35694          * Fires when a cell is double clicked
35695          * @param {Grid} this
35696          * @param {Number} rowIndex
35697          * @param {Number} columnIndex
35698          * @param {Roo.EventObject} e
35699          */
35700         "celldblclick" : true,
35701         /**
35702          * @event rowclick
35703          * Fires when a row is clicked
35704          * @param {Grid} this
35705          * @param {Number} rowIndex
35706          * @param {Roo.EventObject} e
35707          */
35708         "rowclick" : true,
35709         /**
35710          * @event rowdblclick
35711          * Fires when a row is double clicked
35712          * @param {Grid} this
35713          * @param {Number} rowIndex
35714          * @param {Roo.EventObject} e
35715          */
35716         "rowdblclick" : true,
35717         /**
35718          * @event headerclick
35719          * Fires when a header is clicked
35720          * @param {Grid} this
35721          * @param {Number} columnIndex
35722          * @param {Roo.EventObject} e
35723          */
35724         "headerclick" : true,
35725         /**
35726          * @event headerdblclick
35727          * Fires when a header cell is double clicked
35728          * @param {Grid} this
35729          * @param {Number} columnIndex
35730          * @param {Roo.EventObject} e
35731          */
35732         "headerdblclick" : true,
35733         /**
35734          * @event rowcontextmenu
35735          * Fires when a row is right clicked
35736          * @param {Grid} this
35737          * @param {Number} rowIndex
35738          * @param {Roo.EventObject} e
35739          */
35740         "rowcontextmenu" : true,
35741         /**
35742          * @event cellcontextmenu
35743          * Fires when a cell is right clicked
35744          * @param {Grid} this
35745          * @param {Number} rowIndex
35746          * @param {Number} cellIndex
35747          * @param {Roo.EventObject} e
35748          */
35749          "cellcontextmenu" : true,
35750         /**
35751          * @event headercontextmenu
35752          * Fires when a header is right clicked
35753          * @param {Grid} this
35754          * @param {Number} columnIndex
35755          * @param {Roo.EventObject} e
35756          */
35757         "headercontextmenu" : true,
35758         /**
35759          * @event bodyscroll
35760          * Fires when the body element is scrolled
35761          * @param {Number} scrollLeft
35762          * @param {Number} scrollTop
35763          */
35764         "bodyscroll" : true,
35765         /**
35766          * @event columnresize
35767          * Fires when the user resizes a column
35768          * @param {Number} columnIndex
35769          * @param {Number} newSize
35770          */
35771         "columnresize" : true,
35772         /**
35773          * @event columnmove
35774          * Fires when the user moves a column
35775          * @param {Number} oldIndex
35776          * @param {Number} newIndex
35777          */
35778         "columnmove" : true,
35779         /**
35780          * @event startdrag
35781          * Fires when row(s) start being dragged
35782          * @param {Grid} this
35783          * @param {Roo.GridDD} dd The drag drop object
35784          * @param {event} e The raw browser event
35785          */
35786         "startdrag" : true,
35787         /**
35788          * @event enddrag
35789          * Fires when a drag operation is complete
35790          * @param {Grid} this
35791          * @param {Roo.GridDD} dd The drag drop object
35792          * @param {event} e The raw browser event
35793          */
35794         "enddrag" : true,
35795         /**
35796          * @event dragdrop
35797          * Fires when dragged row(s) are dropped on a valid DD target
35798          * @param {Grid} this
35799          * @param {Roo.GridDD} dd The drag drop object
35800          * @param {String} targetId The target drag drop object
35801          * @param {event} e The raw browser event
35802          */
35803         "dragdrop" : true,
35804         /**
35805          * @event dragover
35806          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35807          * @param {Grid} this
35808          * @param {Roo.GridDD} dd The drag drop object
35809          * @param {String} targetId The target drag drop object
35810          * @param {event} e The raw browser event
35811          */
35812         "dragover" : true,
35813         /**
35814          * @event dragenter
35815          *  Fires when the dragged row(s) first cross another DD target while being dragged
35816          * @param {Grid} this
35817          * @param {Roo.GridDD} dd The drag drop object
35818          * @param {String} targetId The target drag drop object
35819          * @param {event} e The raw browser event
35820          */
35821         "dragenter" : true,
35822         /**
35823          * @event dragout
35824          * Fires when the dragged row(s) leave another DD target while being dragged
35825          * @param {Grid} this
35826          * @param {Roo.GridDD} dd The drag drop object
35827          * @param {String} targetId The target drag drop object
35828          * @param {event} e The raw browser event
35829          */
35830         "dragout" : true,
35831         /**
35832          * @event rowclass
35833          * Fires when a row is rendered, so you can change add a style to it.
35834          * @param {GridView} gridview   The grid view
35835          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35836          */
35837         'rowclass' : true,
35838
35839         /**
35840          * @event render
35841          * Fires when the grid is rendered
35842          * @param {Grid} grid
35843          */
35844         'render' : true
35845     });
35846
35847     Roo.grid.Grid.superclass.constructor.call(this);
35848 };
35849 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35850     
35851     /**
35852      * @cfg {String} ddGroup - drag drop group.
35853      */
35854
35855     /**
35856      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35857      */
35858     minColumnWidth : 25,
35859
35860     /**
35861      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35862      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35863      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35864      */
35865     autoSizeColumns : false,
35866
35867     /**
35868      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35869      */
35870     autoSizeHeaders : true,
35871
35872     /**
35873      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35874      */
35875     monitorWindowResize : true,
35876
35877     /**
35878      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35879      * rows measured to get a columns size. Default is 0 (all rows).
35880      */
35881     maxRowsToMeasure : 0,
35882
35883     /**
35884      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35885      */
35886     trackMouseOver : true,
35887
35888     /**
35889     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35890     */
35891     
35892     /**
35893     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35894     */
35895     enableDragDrop : false,
35896     
35897     /**
35898     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35899     */
35900     enableColumnMove : true,
35901     
35902     /**
35903     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35904     */
35905     enableColumnHide : true,
35906     
35907     /**
35908     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35909     */
35910     enableRowHeightSync : false,
35911     
35912     /**
35913     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35914     */
35915     stripeRows : true,
35916     
35917     /**
35918     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35919     */
35920     autoHeight : false,
35921
35922     /**
35923      * @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.
35924      */
35925     autoExpandColumn : false,
35926
35927     /**
35928     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35929     * Default is 50.
35930     */
35931     autoExpandMin : 50,
35932
35933     /**
35934     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35935     */
35936     autoExpandMax : 1000,
35937
35938     /**
35939     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35940     */
35941     view : null,
35942
35943     /**
35944     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35945     */
35946     loadMask : false,
35947     /**
35948     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35949     */
35950     dropTarget: false,
35951     
35952    
35953     
35954     // private
35955     rendered : false,
35956
35957     /**
35958     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35959     * of a fixed width. Default is false.
35960     */
35961     /**
35962     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35963     */
35964     /**
35965      * Called once after all setup has been completed and the grid is ready to be rendered.
35966      * @return {Roo.grid.Grid} this
35967      */
35968     render : function()
35969     {
35970         var c = this.container;
35971         // try to detect autoHeight/width mode
35972         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35973             this.autoHeight = true;
35974         }
35975         var view = this.getView();
35976         view.init(this);
35977
35978         c.on("click", this.onClick, this);
35979         c.on("dblclick", this.onDblClick, this);
35980         c.on("contextmenu", this.onContextMenu, this);
35981         c.on("keydown", this.onKeyDown, this);
35982         if (Roo.isTouch) {
35983             c.on("touchstart", this.onTouchStart, this);
35984         }
35985
35986         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35987
35988         this.getSelectionModel().init(this);
35989
35990         view.render();
35991
35992         if(this.loadMask){
35993             this.loadMask = new Roo.LoadMask(this.container,
35994                     Roo.apply({store:this.dataSource}, this.loadMask));
35995         }
35996         
35997         
35998         if (this.toolbar && this.toolbar.xtype) {
35999             this.toolbar.container = this.getView().getHeaderPanel(true);
36000             this.toolbar = new Roo.Toolbar(this.toolbar);
36001         }
36002         if (this.footer && this.footer.xtype) {
36003             this.footer.dataSource = this.getDataSource();
36004             this.footer.container = this.getView().getFooterPanel(true);
36005             this.footer = Roo.factory(this.footer, Roo);
36006         }
36007         if (this.dropTarget && this.dropTarget.xtype) {
36008             delete this.dropTarget.xtype;
36009             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36010         }
36011         
36012         
36013         this.rendered = true;
36014         this.fireEvent('render', this);
36015         return this;
36016     },
36017
36018         /**
36019          * Reconfigures the grid to use a different Store and Column Model.
36020          * The View will be bound to the new objects and refreshed.
36021          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36022          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36023          */
36024     reconfigure : function(dataSource, colModel){
36025         if(this.loadMask){
36026             this.loadMask.destroy();
36027             this.loadMask = new Roo.LoadMask(this.container,
36028                     Roo.apply({store:dataSource}, this.loadMask));
36029         }
36030         this.view.bind(dataSource, colModel);
36031         this.dataSource = dataSource;
36032         this.colModel = colModel;
36033         this.view.refresh(true);
36034     },
36035
36036     // private
36037     onKeyDown : function(e){
36038         this.fireEvent("keydown", e);
36039     },
36040
36041     /**
36042      * Destroy this grid.
36043      * @param {Boolean} removeEl True to remove the element
36044      */
36045     destroy : function(removeEl, keepListeners){
36046         if(this.loadMask){
36047             this.loadMask.destroy();
36048         }
36049         var c = this.container;
36050         c.removeAllListeners();
36051         this.view.destroy();
36052         this.colModel.purgeListeners();
36053         if(!keepListeners){
36054             this.purgeListeners();
36055         }
36056         c.update("");
36057         if(removeEl === true){
36058             c.remove();
36059         }
36060     },
36061
36062     // private
36063     processEvent : function(name, e){
36064         // does this fire select???
36065         Roo.log('grid:processEvent '  + name);
36066         
36067         if (name != 'touchstart' ) {
36068             this.fireEvent(name, e);    
36069         }
36070         
36071         var t = e.getTarget();
36072         var v = this.view;
36073         var header = v.findHeaderIndex(t);
36074         if(header !== false){
36075             var ename = name == 'touchstart' ? 'click' : name;
36076              
36077             this.fireEvent("header" + ename, this, header, e);
36078         }else{
36079             var row = v.findRowIndex(t);
36080             var cell = v.findCellIndex(t);
36081             if (name == 'touchstart') {
36082                 // first touch is always a click.
36083                 // hopefull this happens after selection is updated.?
36084                 name = false;
36085                 
36086                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36087                     var cs = this.selModel.getSelectedCell();
36088                     if (row == cs[0] && cell == cs[1]){
36089                         name = 'dblclick';
36090                     }
36091                 }
36092                 if (typeof(this.selModel.getSelections) != 'undefined') {
36093                     var cs = this.selModel.getSelections();
36094                     var ds = this.dataSource;
36095                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36096                         name = 'dblclick';
36097                     }
36098                 }
36099                 if (!name) {
36100                     return;
36101                 }
36102             }
36103             
36104             
36105             if(row !== false){
36106                 this.fireEvent("row" + name, this, row, e);
36107                 if(cell !== false){
36108                     this.fireEvent("cell" + name, this, row, cell, e);
36109                 }
36110             }
36111         }
36112     },
36113
36114     // private
36115     onClick : function(e){
36116         this.processEvent("click", e);
36117     },
36118    // private
36119     onTouchStart : function(e){
36120         this.processEvent("touchstart", e);
36121     },
36122
36123     // private
36124     onContextMenu : function(e, t){
36125         this.processEvent("contextmenu", e);
36126     },
36127
36128     // private
36129     onDblClick : function(e){
36130         this.processEvent("dblclick", e);
36131     },
36132
36133     // private
36134     walkCells : function(row, col, step, fn, scope){
36135         var cm = this.colModel, clen = cm.getColumnCount();
36136         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36137         if(step < 0){
36138             if(col < 0){
36139                 row--;
36140                 first = false;
36141             }
36142             while(row >= 0){
36143                 if(!first){
36144                     col = clen-1;
36145                 }
36146                 first = false;
36147                 while(col >= 0){
36148                     if(fn.call(scope || this, row, col, cm) === true){
36149                         return [row, col];
36150                     }
36151                     col--;
36152                 }
36153                 row--;
36154             }
36155         } else {
36156             if(col >= clen){
36157                 row++;
36158                 first = false;
36159             }
36160             while(row < rlen){
36161                 if(!first){
36162                     col = 0;
36163                 }
36164                 first = false;
36165                 while(col < clen){
36166                     if(fn.call(scope || this, row, col, cm) === true){
36167                         return [row, col];
36168                     }
36169                     col++;
36170                 }
36171                 row++;
36172             }
36173         }
36174         return null;
36175     },
36176
36177     // private
36178     getSelections : function(){
36179         return this.selModel.getSelections();
36180     },
36181
36182     /**
36183      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36184      * but if manual update is required this method will initiate it.
36185      */
36186     autoSize : function(){
36187         if(this.rendered){
36188             this.view.layout();
36189             if(this.view.adjustForScroll){
36190                 this.view.adjustForScroll();
36191             }
36192         }
36193     },
36194
36195     /**
36196      * Returns the grid's underlying element.
36197      * @return {Element} The element
36198      */
36199     getGridEl : function(){
36200         return this.container;
36201     },
36202
36203     // private for compatibility, overridden by editor grid
36204     stopEditing : function(){},
36205
36206     /**
36207      * Returns the grid's SelectionModel.
36208      * @return {SelectionModel}
36209      */
36210     getSelectionModel : function(){
36211         if(!this.selModel){
36212             this.selModel = new Roo.grid.RowSelectionModel();
36213         }
36214         return this.selModel;
36215     },
36216
36217     /**
36218      * Returns the grid's DataSource.
36219      * @return {DataSource}
36220      */
36221     getDataSource : function(){
36222         return this.dataSource;
36223     },
36224
36225     /**
36226      * Returns the grid's ColumnModel.
36227      * @return {ColumnModel}
36228      */
36229     getColumnModel : function(){
36230         return this.colModel;
36231     },
36232
36233     /**
36234      * Returns the grid's GridView object.
36235      * @return {GridView}
36236      */
36237     getView : function(){
36238         if(!this.view){
36239             this.view = new Roo.grid.GridView(this.viewConfig);
36240         }
36241         return this.view;
36242     },
36243     /**
36244      * Called to get grid's drag proxy text, by default returns this.ddText.
36245      * @return {String}
36246      */
36247     getDragDropText : function(){
36248         var count = this.selModel.getCount();
36249         return String.format(this.ddText, count, count == 1 ? '' : 's');
36250     }
36251 });
36252 /**
36253  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36254  * %0 is replaced with the number of selected rows.
36255  * @type String
36256  */
36257 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36258  * Based on:
36259  * Ext JS Library 1.1.1
36260  * Copyright(c) 2006-2007, Ext JS, LLC.
36261  *
36262  * Originally Released Under LGPL - original licence link has changed is not relivant.
36263  *
36264  * Fork - LGPL
36265  * <script type="text/javascript">
36266  */
36267  
36268 Roo.grid.AbstractGridView = function(){
36269         this.grid = null;
36270         
36271         this.events = {
36272             "beforerowremoved" : true,
36273             "beforerowsinserted" : true,
36274             "beforerefresh" : true,
36275             "rowremoved" : true,
36276             "rowsinserted" : true,
36277             "rowupdated" : true,
36278             "refresh" : true
36279         };
36280     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36281 };
36282
36283 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36284     rowClass : "x-grid-row",
36285     cellClass : "x-grid-cell",
36286     tdClass : "x-grid-td",
36287     hdClass : "x-grid-hd",
36288     splitClass : "x-grid-hd-split",
36289     
36290     init: function(grid){
36291         this.grid = grid;
36292                 var cid = this.grid.getGridEl().id;
36293         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36294         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36295         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36296         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36297         },
36298         
36299     getColumnRenderers : function(){
36300         var renderers = [];
36301         var cm = this.grid.colModel;
36302         var colCount = cm.getColumnCount();
36303         for(var i = 0; i < colCount; i++){
36304             renderers[i] = cm.getRenderer(i);
36305         }
36306         return renderers;
36307     },
36308     
36309     getColumnIds : function(){
36310         var ids = [];
36311         var cm = this.grid.colModel;
36312         var colCount = cm.getColumnCount();
36313         for(var i = 0; i < colCount; i++){
36314             ids[i] = cm.getColumnId(i);
36315         }
36316         return ids;
36317     },
36318     
36319     getDataIndexes : function(){
36320         if(!this.indexMap){
36321             this.indexMap = this.buildIndexMap();
36322         }
36323         return this.indexMap.colToData;
36324     },
36325     
36326     getColumnIndexByDataIndex : function(dataIndex){
36327         if(!this.indexMap){
36328             this.indexMap = this.buildIndexMap();
36329         }
36330         return this.indexMap.dataToCol[dataIndex];
36331     },
36332     
36333     /**
36334      * Set a css style for a column dynamically. 
36335      * @param {Number} colIndex The index of the column
36336      * @param {String} name The css property name
36337      * @param {String} value The css value
36338      */
36339     setCSSStyle : function(colIndex, name, value){
36340         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36341         Roo.util.CSS.updateRule(selector, name, value);
36342     },
36343     
36344     generateRules : function(cm){
36345         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36346         Roo.util.CSS.removeStyleSheet(rulesId);
36347         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36348             var cid = cm.getColumnId(i);
36349             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36350                          this.tdSelector, cid, " {\n}\n",
36351                          this.hdSelector, cid, " {\n}\n",
36352                          this.splitSelector, cid, " {\n}\n");
36353         }
36354         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36355     }
36356 });/*
36357  * Based on:
36358  * Ext JS Library 1.1.1
36359  * Copyright(c) 2006-2007, Ext JS, LLC.
36360  *
36361  * Originally Released Under LGPL - original licence link has changed is not relivant.
36362  *
36363  * Fork - LGPL
36364  * <script type="text/javascript">
36365  */
36366
36367 // private
36368 // This is a support class used internally by the Grid components
36369 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36370     this.grid = grid;
36371     this.view = grid.getView();
36372     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36373     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36374     if(hd2){
36375         this.setHandleElId(Roo.id(hd));
36376         this.setOuterHandleElId(Roo.id(hd2));
36377     }
36378     this.scroll = false;
36379 };
36380 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36381     maxDragWidth: 120,
36382     getDragData : function(e){
36383         var t = Roo.lib.Event.getTarget(e);
36384         var h = this.view.findHeaderCell(t);
36385         if(h){
36386             return {ddel: h.firstChild, header:h};
36387         }
36388         return false;
36389     },
36390
36391     onInitDrag : function(e){
36392         this.view.headersDisabled = true;
36393         var clone = this.dragData.ddel.cloneNode(true);
36394         clone.id = Roo.id();
36395         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36396         this.proxy.update(clone);
36397         return true;
36398     },
36399
36400     afterValidDrop : function(){
36401         var v = this.view;
36402         setTimeout(function(){
36403             v.headersDisabled = false;
36404         }, 50);
36405     },
36406
36407     afterInvalidDrop : function(){
36408         var v = this.view;
36409         setTimeout(function(){
36410             v.headersDisabled = false;
36411         }, 50);
36412     }
36413 });
36414 /*
36415  * Based on:
36416  * Ext JS Library 1.1.1
36417  * Copyright(c) 2006-2007, Ext JS, LLC.
36418  *
36419  * Originally Released Under LGPL - original licence link has changed is not relivant.
36420  *
36421  * Fork - LGPL
36422  * <script type="text/javascript">
36423  */
36424 // private
36425 // This is a support class used internally by the Grid components
36426 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36427     this.grid = grid;
36428     this.view = grid.getView();
36429     // split the proxies so they don't interfere with mouse events
36430     this.proxyTop = Roo.DomHelper.append(document.body, {
36431         cls:"col-move-top", html:"&#160;"
36432     }, true);
36433     this.proxyBottom = Roo.DomHelper.append(document.body, {
36434         cls:"col-move-bottom", html:"&#160;"
36435     }, true);
36436     this.proxyTop.hide = this.proxyBottom.hide = function(){
36437         this.setLeftTop(-100,-100);
36438         this.setStyle("visibility", "hidden");
36439     };
36440     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36441     // temporarily disabled
36442     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36443     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36444 };
36445 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36446     proxyOffsets : [-4, -9],
36447     fly: Roo.Element.fly,
36448
36449     getTargetFromEvent : function(e){
36450         var t = Roo.lib.Event.getTarget(e);
36451         var cindex = this.view.findCellIndex(t);
36452         if(cindex !== false){
36453             return this.view.getHeaderCell(cindex);
36454         }
36455         return null;
36456     },
36457
36458     nextVisible : function(h){
36459         var v = this.view, cm = this.grid.colModel;
36460         h = h.nextSibling;
36461         while(h){
36462             if(!cm.isHidden(v.getCellIndex(h))){
36463                 return h;
36464             }
36465             h = h.nextSibling;
36466         }
36467         return null;
36468     },
36469
36470     prevVisible : function(h){
36471         var v = this.view, cm = this.grid.colModel;
36472         h = h.prevSibling;
36473         while(h){
36474             if(!cm.isHidden(v.getCellIndex(h))){
36475                 return h;
36476             }
36477             h = h.prevSibling;
36478         }
36479         return null;
36480     },
36481
36482     positionIndicator : function(h, n, e){
36483         var x = Roo.lib.Event.getPageX(e);
36484         var r = Roo.lib.Dom.getRegion(n.firstChild);
36485         var px, pt, py = r.top + this.proxyOffsets[1];
36486         if((r.right - x) <= (r.right-r.left)/2){
36487             px = r.right+this.view.borderWidth;
36488             pt = "after";
36489         }else{
36490             px = r.left;
36491             pt = "before";
36492         }
36493         var oldIndex = this.view.getCellIndex(h);
36494         var newIndex = this.view.getCellIndex(n);
36495
36496         if(this.grid.colModel.isFixed(newIndex)){
36497             return false;
36498         }
36499
36500         var locked = this.grid.colModel.isLocked(newIndex);
36501
36502         if(pt == "after"){
36503             newIndex++;
36504         }
36505         if(oldIndex < newIndex){
36506             newIndex--;
36507         }
36508         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36509             return false;
36510         }
36511         px +=  this.proxyOffsets[0];
36512         this.proxyTop.setLeftTop(px, py);
36513         this.proxyTop.show();
36514         if(!this.bottomOffset){
36515             this.bottomOffset = this.view.mainHd.getHeight();
36516         }
36517         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36518         this.proxyBottom.show();
36519         return pt;
36520     },
36521
36522     onNodeEnter : function(n, dd, e, data){
36523         if(data.header != n){
36524             this.positionIndicator(data.header, n, e);
36525         }
36526     },
36527
36528     onNodeOver : function(n, dd, e, data){
36529         var result = false;
36530         if(data.header != n){
36531             result = this.positionIndicator(data.header, n, e);
36532         }
36533         if(!result){
36534             this.proxyTop.hide();
36535             this.proxyBottom.hide();
36536         }
36537         return result ? this.dropAllowed : this.dropNotAllowed;
36538     },
36539
36540     onNodeOut : function(n, dd, e, data){
36541         this.proxyTop.hide();
36542         this.proxyBottom.hide();
36543     },
36544
36545     onNodeDrop : function(n, dd, e, data){
36546         var h = data.header;
36547         if(h != n){
36548             var cm = this.grid.colModel;
36549             var x = Roo.lib.Event.getPageX(e);
36550             var r = Roo.lib.Dom.getRegion(n.firstChild);
36551             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36552             var oldIndex = this.view.getCellIndex(h);
36553             var newIndex = this.view.getCellIndex(n);
36554             var locked = cm.isLocked(newIndex);
36555             if(pt == "after"){
36556                 newIndex++;
36557             }
36558             if(oldIndex < newIndex){
36559                 newIndex--;
36560             }
36561             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36562                 return false;
36563             }
36564             cm.setLocked(oldIndex, locked, true);
36565             cm.moveColumn(oldIndex, newIndex);
36566             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36567             return true;
36568         }
36569         return false;
36570     }
36571 });
36572 /*
36573  * Based on:
36574  * Ext JS Library 1.1.1
36575  * Copyright(c) 2006-2007, Ext JS, LLC.
36576  *
36577  * Originally Released Under LGPL - original licence link has changed is not relivant.
36578  *
36579  * Fork - LGPL
36580  * <script type="text/javascript">
36581  */
36582   
36583 /**
36584  * @class Roo.grid.GridView
36585  * @extends Roo.util.Observable
36586  *
36587  * @constructor
36588  * @param {Object} config
36589  */
36590 Roo.grid.GridView = function(config){
36591     Roo.grid.GridView.superclass.constructor.call(this);
36592     this.el = null;
36593
36594     Roo.apply(this, config);
36595 };
36596
36597 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36598
36599     unselectable :  'unselectable="on"',
36600     unselectableCls :  'x-unselectable',
36601     
36602     
36603     rowClass : "x-grid-row",
36604
36605     cellClass : "x-grid-col",
36606
36607     tdClass : "x-grid-td",
36608
36609     hdClass : "x-grid-hd",
36610
36611     splitClass : "x-grid-split",
36612
36613     sortClasses : ["sort-asc", "sort-desc"],
36614
36615     enableMoveAnim : false,
36616
36617     hlColor: "C3DAF9",
36618
36619     dh : Roo.DomHelper,
36620
36621     fly : Roo.Element.fly,
36622
36623     css : Roo.util.CSS,
36624
36625     borderWidth: 1,
36626
36627     splitOffset: 3,
36628
36629     scrollIncrement : 22,
36630
36631     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36632
36633     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36634
36635     bind : function(ds, cm){
36636         if(this.ds){
36637             this.ds.un("load", this.onLoad, this);
36638             this.ds.un("datachanged", this.onDataChange, this);
36639             this.ds.un("add", this.onAdd, this);
36640             this.ds.un("remove", this.onRemove, this);
36641             this.ds.un("update", this.onUpdate, this);
36642             this.ds.un("clear", this.onClear, this);
36643         }
36644         if(ds){
36645             ds.on("load", this.onLoad, this);
36646             ds.on("datachanged", this.onDataChange, this);
36647             ds.on("add", this.onAdd, this);
36648             ds.on("remove", this.onRemove, this);
36649             ds.on("update", this.onUpdate, this);
36650             ds.on("clear", this.onClear, this);
36651         }
36652         this.ds = ds;
36653
36654         if(this.cm){
36655             this.cm.un("widthchange", this.onColWidthChange, this);
36656             this.cm.un("headerchange", this.onHeaderChange, this);
36657             this.cm.un("hiddenchange", this.onHiddenChange, this);
36658             this.cm.un("columnmoved", this.onColumnMove, this);
36659             this.cm.un("columnlockchange", this.onColumnLock, this);
36660         }
36661         if(cm){
36662             this.generateRules(cm);
36663             cm.on("widthchange", this.onColWidthChange, this);
36664             cm.on("headerchange", this.onHeaderChange, this);
36665             cm.on("hiddenchange", this.onHiddenChange, this);
36666             cm.on("columnmoved", this.onColumnMove, this);
36667             cm.on("columnlockchange", this.onColumnLock, this);
36668         }
36669         this.cm = cm;
36670     },
36671
36672     init: function(grid){
36673         Roo.grid.GridView.superclass.init.call(this, grid);
36674
36675         this.bind(grid.dataSource, grid.colModel);
36676
36677         grid.on("headerclick", this.handleHeaderClick, this);
36678
36679         if(grid.trackMouseOver){
36680             grid.on("mouseover", this.onRowOver, this);
36681             grid.on("mouseout", this.onRowOut, this);
36682         }
36683         grid.cancelTextSelection = function(){};
36684         this.gridId = grid.id;
36685
36686         var tpls = this.templates || {};
36687
36688         if(!tpls.master){
36689             tpls.master = new Roo.Template(
36690                '<div class="x-grid" hidefocus="true">',
36691                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36692                   '<div class="x-grid-topbar"></div>',
36693                   '<div class="x-grid-scroller"><div></div></div>',
36694                   '<div class="x-grid-locked">',
36695                       '<div class="x-grid-header">{lockedHeader}</div>',
36696                       '<div class="x-grid-body">{lockedBody}</div>',
36697                   "</div>",
36698                   '<div class="x-grid-viewport">',
36699                       '<div class="x-grid-header">{header}</div>',
36700                       '<div class="x-grid-body">{body}</div>',
36701                   "</div>",
36702                   '<div class="x-grid-bottombar"></div>',
36703                  
36704                   '<div class="x-grid-resize-proxy">&#160;</div>',
36705                "</div>"
36706             );
36707             tpls.master.disableformats = true;
36708         }
36709
36710         if(!tpls.header){
36711             tpls.header = new Roo.Template(
36712                '<table border="0" cellspacing="0" cellpadding="0">',
36713                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36714                "</table>{splits}"
36715             );
36716             tpls.header.disableformats = true;
36717         }
36718         tpls.header.compile();
36719
36720         if(!tpls.hcell){
36721             tpls.hcell = new Roo.Template(
36722                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36723                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36724                 "</div></td>"
36725              );
36726              tpls.hcell.disableFormats = true;
36727         }
36728         tpls.hcell.compile();
36729
36730         if(!tpls.hsplit){
36731             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36732                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36733             tpls.hsplit.disableFormats = true;
36734         }
36735         tpls.hsplit.compile();
36736
36737         if(!tpls.body){
36738             tpls.body = new Roo.Template(
36739                '<table border="0" cellspacing="0" cellpadding="0">',
36740                "<tbody>{rows}</tbody>",
36741                "</table>"
36742             );
36743             tpls.body.disableFormats = true;
36744         }
36745         tpls.body.compile();
36746
36747         if(!tpls.row){
36748             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36749             tpls.row.disableFormats = true;
36750         }
36751         tpls.row.compile();
36752
36753         if(!tpls.cell){
36754             tpls.cell = new Roo.Template(
36755                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36756                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36757                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36758                 "</td>"
36759             );
36760             tpls.cell.disableFormats = true;
36761         }
36762         tpls.cell.compile();
36763
36764         this.templates = tpls;
36765     },
36766
36767     // remap these for backwards compat
36768     onColWidthChange : function(){
36769         this.updateColumns.apply(this, arguments);
36770     },
36771     onHeaderChange : function(){
36772         this.updateHeaders.apply(this, arguments);
36773     }, 
36774     onHiddenChange : function(){
36775         this.handleHiddenChange.apply(this, arguments);
36776     },
36777     onColumnMove : function(){
36778         this.handleColumnMove.apply(this, arguments);
36779     },
36780     onColumnLock : function(){
36781         this.handleLockChange.apply(this, arguments);
36782     },
36783
36784     onDataChange : function(){
36785         this.refresh();
36786         this.updateHeaderSortState();
36787     },
36788
36789     onClear : function(){
36790         this.refresh();
36791     },
36792
36793     onUpdate : function(ds, record){
36794         this.refreshRow(record);
36795     },
36796
36797     refreshRow : function(record){
36798         var ds = this.ds, index;
36799         if(typeof record == 'number'){
36800             index = record;
36801             record = ds.getAt(index);
36802         }else{
36803             index = ds.indexOf(record);
36804         }
36805         this.insertRows(ds, index, index, true);
36806         this.onRemove(ds, record, index+1, true);
36807         this.syncRowHeights(index, index);
36808         this.layout();
36809         this.fireEvent("rowupdated", this, index, record);
36810     },
36811
36812     onAdd : function(ds, records, index){
36813         this.insertRows(ds, index, index + (records.length-1));
36814     },
36815
36816     onRemove : function(ds, record, index, isUpdate){
36817         if(isUpdate !== true){
36818             this.fireEvent("beforerowremoved", this, index, record);
36819         }
36820         var bt = this.getBodyTable(), lt = this.getLockedTable();
36821         if(bt.rows[index]){
36822             bt.firstChild.removeChild(bt.rows[index]);
36823         }
36824         if(lt.rows[index]){
36825             lt.firstChild.removeChild(lt.rows[index]);
36826         }
36827         if(isUpdate !== true){
36828             this.stripeRows(index);
36829             this.syncRowHeights(index, index);
36830             this.layout();
36831             this.fireEvent("rowremoved", this, index, record);
36832         }
36833     },
36834
36835     onLoad : function(){
36836         this.scrollToTop();
36837     },
36838
36839     /**
36840      * Scrolls the grid to the top
36841      */
36842     scrollToTop : function(){
36843         if(this.scroller){
36844             this.scroller.dom.scrollTop = 0;
36845             this.syncScroll();
36846         }
36847     },
36848
36849     /**
36850      * Gets a panel in the header of the grid that can be used for toolbars etc.
36851      * After modifying the contents of this panel a call to grid.autoSize() may be
36852      * required to register any changes in size.
36853      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36854      * @return Roo.Element
36855      */
36856     getHeaderPanel : function(doShow){
36857         if(doShow){
36858             this.headerPanel.show();
36859         }
36860         return this.headerPanel;
36861     },
36862
36863     /**
36864      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36865      * After modifying the contents of this panel a call to grid.autoSize() may be
36866      * required to register any changes in size.
36867      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36868      * @return Roo.Element
36869      */
36870     getFooterPanel : function(doShow){
36871         if(doShow){
36872             this.footerPanel.show();
36873         }
36874         return this.footerPanel;
36875     },
36876
36877     initElements : function(){
36878         var E = Roo.Element;
36879         var el = this.grid.getGridEl().dom.firstChild;
36880         var cs = el.childNodes;
36881
36882         this.el = new E(el);
36883         
36884          this.focusEl = new E(el.firstChild);
36885         this.focusEl.swallowEvent("click", true);
36886         
36887         this.headerPanel = new E(cs[1]);
36888         this.headerPanel.enableDisplayMode("block");
36889
36890         this.scroller = new E(cs[2]);
36891         this.scrollSizer = new E(this.scroller.dom.firstChild);
36892
36893         this.lockedWrap = new E(cs[3]);
36894         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36895         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36896
36897         this.mainWrap = new E(cs[4]);
36898         this.mainHd = new E(this.mainWrap.dom.firstChild);
36899         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36900
36901         this.footerPanel = new E(cs[5]);
36902         this.footerPanel.enableDisplayMode("block");
36903
36904         this.resizeProxy = new E(cs[6]);
36905
36906         this.headerSelector = String.format(
36907            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36908            this.lockedHd.id, this.mainHd.id
36909         );
36910
36911         this.splitterSelector = String.format(
36912            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36913            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36914         );
36915     },
36916     idToCssName : function(s)
36917     {
36918         return s.replace(/[^a-z0-9]+/ig, '-');
36919     },
36920
36921     getHeaderCell : function(index){
36922         return Roo.DomQuery.select(this.headerSelector)[index];
36923     },
36924
36925     getHeaderCellMeasure : function(index){
36926         return this.getHeaderCell(index).firstChild;
36927     },
36928
36929     getHeaderCellText : function(index){
36930         return this.getHeaderCell(index).firstChild.firstChild;
36931     },
36932
36933     getLockedTable : function(){
36934         return this.lockedBody.dom.firstChild;
36935     },
36936
36937     getBodyTable : function(){
36938         return this.mainBody.dom.firstChild;
36939     },
36940
36941     getLockedRow : function(index){
36942         return this.getLockedTable().rows[index];
36943     },
36944
36945     getRow : function(index){
36946         return this.getBodyTable().rows[index];
36947     },
36948
36949     getRowComposite : function(index){
36950         if(!this.rowEl){
36951             this.rowEl = new Roo.CompositeElementLite();
36952         }
36953         var els = [], lrow, mrow;
36954         if(lrow = this.getLockedRow(index)){
36955             els.push(lrow);
36956         }
36957         if(mrow = this.getRow(index)){
36958             els.push(mrow);
36959         }
36960         this.rowEl.elements = els;
36961         return this.rowEl;
36962     },
36963     /**
36964      * Gets the 'td' of the cell
36965      * 
36966      * @param {Integer} rowIndex row to select
36967      * @param {Integer} colIndex column to select
36968      * 
36969      * @return {Object} 
36970      */
36971     getCell : function(rowIndex, colIndex){
36972         var locked = this.cm.getLockedCount();
36973         var source;
36974         if(colIndex < locked){
36975             source = this.lockedBody.dom.firstChild;
36976         }else{
36977             source = this.mainBody.dom.firstChild;
36978             colIndex -= locked;
36979         }
36980         return source.rows[rowIndex].childNodes[colIndex];
36981     },
36982
36983     getCellText : function(rowIndex, colIndex){
36984         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36985     },
36986
36987     getCellBox : function(cell){
36988         var b = this.fly(cell).getBox();
36989         if(Roo.isOpera){ // opera fails to report the Y
36990             b.y = cell.offsetTop + this.mainBody.getY();
36991         }
36992         return b;
36993     },
36994
36995     getCellIndex : function(cell){
36996         var id = String(cell.className).match(this.cellRE);
36997         if(id){
36998             return parseInt(id[1], 10);
36999         }
37000         return 0;
37001     },
37002
37003     findHeaderIndex : function(n){
37004         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37005         return r ? this.getCellIndex(r) : false;
37006     },
37007
37008     findHeaderCell : function(n){
37009         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37010         return r ? r : false;
37011     },
37012
37013     findRowIndex : function(n){
37014         if(!n){
37015             return false;
37016         }
37017         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37018         return r ? r.rowIndex : false;
37019     },
37020
37021     findCellIndex : function(node){
37022         var stop = this.el.dom;
37023         while(node && node != stop){
37024             if(this.findRE.test(node.className)){
37025                 return this.getCellIndex(node);
37026             }
37027             node = node.parentNode;
37028         }
37029         return false;
37030     },
37031
37032     getColumnId : function(index){
37033         return this.cm.getColumnId(index);
37034     },
37035
37036     getSplitters : function()
37037     {
37038         if(this.splitterSelector){
37039            return Roo.DomQuery.select(this.splitterSelector);
37040         }else{
37041             return null;
37042       }
37043     },
37044
37045     getSplitter : function(index){
37046         return this.getSplitters()[index];
37047     },
37048
37049     onRowOver : function(e, t){
37050         var row;
37051         if((row = this.findRowIndex(t)) !== false){
37052             this.getRowComposite(row).addClass("x-grid-row-over");
37053         }
37054     },
37055
37056     onRowOut : function(e, t){
37057         var row;
37058         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37059             this.getRowComposite(row).removeClass("x-grid-row-over");
37060         }
37061     },
37062
37063     renderHeaders : function(){
37064         var cm = this.cm;
37065         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37066         var cb = [], lb = [], sb = [], lsb = [], p = {};
37067         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37068             p.cellId = "x-grid-hd-0-" + i;
37069             p.splitId = "x-grid-csplit-0-" + i;
37070             p.id = cm.getColumnId(i);
37071             p.title = cm.getColumnTooltip(i) || "";
37072             p.value = cm.getColumnHeader(i) || "";
37073             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37074             if(!cm.isLocked(i)){
37075                 cb[cb.length] = ct.apply(p);
37076                 sb[sb.length] = st.apply(p);
37077             }else{
37078                 lb[lb.length] = ct.apply(p);
37079                 lsb[lsb.length] = st.apply(p);
37080             }
37081         }
37082         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37083                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37084     },
37085
37086     updateHeaders : function(){
37087         var html = this.renderHeaders();
37088         this.lockedHd.update(html[0]);
37089         this.mainHd.update(html[1]);
37090     },
37091
37092     /**
37093      * Focuses the specified row.
37094      * @param {Number} row The row index
37095      */
37096     focusRow : function(row)
37097     {
37098         //Roo.log('GridView.focusRow');
37099         var x = this.scroller.dom.scrollLeft;
37100         this.focusCell(row, 0, false);
37101         this.scroller.dom.scrollLeft = x;
37102     },
37103
37104     /**
37105      * Focuses the specified cell.
37106      * @param {Number} row The row index
37107      * @param {Number} col The column index
37108      * @param {Boolean} hscroll false to disable horizontal scrolling
37109      */
37110     focusCell : function(row, col, hscroll)
37111     {
37112         //Roo.log('GridView.focusCell');
37113         var el = this.ensureVisible(row, col, hscroll);
37114         this.focusEl.alignTo(el, "tl-tl");
37115         if(Roo.isGecko){
37116             this.focusEl.focus();
37117         }else{
37118             this.focusEl.focus.defer(1, this.focusEl);
37119         }
37120     },
37121
37122     /**
37123      * Scrolls the specified cell into view
37124      * @param {Number} row The row index
37125      * @param {Number} col The column index
37126      * @param {Boolean} hscroll false to disable horizontal scrolling
37127      */
37128     ensureVisible : function(row, col, hscroll)
37129     {
37130         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37131         //return null; //disable for testing.
37132         if(typeof row != "number"){
37133             row = row.rowIndex;
37134         }
37135         if(row < 0 && row >= this.ds.getCount()){
37136             return  null;
37137         }
37138         col = (col !== undefined ? col : 0);
37139         var cm = this.grid.colModel;
37140         while(cm.isHidden(col)){
37141             col++;
37142         }
37143
37144         var el = this.getCell(row, col);
37145         if(!el){
37146             return null;
37147         }
37148         var c = this.scroller.dom;
37149
37150         var ctop = parseInt(el.offsetTop, 10);
37151         var cleft = parseInt(el.offsetLeft, 10);
37152         var cbot = ctop + el.offsetHeight;
37153         var cright = cleft + el.offsetWidth;
37154         
37155         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37156         var stop = parseInt(c.scrollTop, 10);
37157         var sleft = parseInt(c.scrollLeft, 10);
37158         var sbot = stop + ch;
37159         var sright = sleft + c.clientWidth;
37160         /*
37161         Roo.log('GridView.ensureVisible:' +
37162                 ' ctop:' + ctop +
37163                 ' c.clientHeight:' + c.clientHeight +
37164                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37165                 ' stop:' + stop +
37166                 ' cbot:' + cbot +
37167                 ' sbot:' + sbot +
37168                 ' ch:' + ch  
37169                 );
37170         */
37171         if(ctop < stop){
37172              c.scrollTop = ctop;
37173             //Roo.log("set scrolltop to ctop DISABLE?");
37174         }else if(cbot > sbot){
37175             //Roo.log("set scrolltop to cbot-ch");
37176             c.scrollTop = cbot-ch;
37177         }
37178         
37179         if(hscroll !== false){
37180             if(cleft < sleft){
37181                 c.scrollLeft = cleft;
37182             }else if(cright > sright){
37183                 c.scrollLeft = cright-c.clientWidth;
37184             }
37185         }
37186          
37187         return el;
37188     },
37189
37190     updateColumns : function(){
37191         this.grid.stopEditing();
37192         var cm = this.grid.colModel, colIds = this.getColumnIds();
37193         //var totalWidth = cm.getTotalWidth();
37194         var pos = 0;
37195         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37196             //if(cm.isHidden(i)) continue;
37197             var w = cm.getColumnWidth(i);
37198             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37199             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37200         }
37201         this.updateSplitters();
37202     },
37203
37204     generateRules : function(cm){
37205         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37206         Roo.util.CSS.removeStyleSheet(rulesId);
37207         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37208             var cid = cm.getColumnId(i);
37209             var align = '';
37210             if(cm.config[i].align){
37211                 align = 'text-align:'+cm.config[i].align+';';
37212             }
37213             var hidden = '';
37214             if(cm.isHidden(i)){
37215                 hidden = 'display:none;';
37216             }
37217             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37218             ruleBuf.push(
37219                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37220                     this.hdSelector, cid, " {\n", align, width, "}\n",
37221                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37222                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37223         }
37224         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37225     },
37226
37227     updateSplitters : function(){
37228         var cm = this.cm, s = this.getSplitters();
37229         if(s){ // splitters not created yet
37230             var pos = 0, locked = true;
37231             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37232                 if(cm.isHidden(i)) continue;
37233                 var w = cm.getColumnWidth(i); // make sure it's a number
37234                 if(!cm.isLocked(i) && locked){
37235                     pos = 0;
37236                     locked = false;
37237                 }
37238                 pos += w;
37239                 s[i].style.left = (pos-this.splitOffset) + "px";
37240             }
37241         }
37242     },
37243
37244     handleHiddenChange : function(colModel, colIndex, hidden){
37245         if(hidden){
37246             this.hideColumn(colIndex);
37247         }else{
37248             this.unhideColumn(colIndex);
37249         }
37250     },
37251
37252     hideColumn : function(colIndex){
37253         var cid = this.getColumnId(colIndex);
37254         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37255         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37256         if(Roo.isSafari){
37257             this.updateHeaders();
37258         }
37259         this.updateSplitters();
37260         this.layout();
37261     },
37262
37263     unhideColumn : function(colIndex){
37264         var cid = this.getColumnId(colIndex);
37265         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37266         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37267
37268         if(Roo.isSafari){
37269             this.updateHeaders();
37270         }
37271         this.updateSplitters();
37272         this.layout();
37273     },
37274
37275     insertRows : function(dm, firstRow, lastRow, isUpdate){
37276         if(firstRow == 0 && lastRow == dm.getCount()-1){
37277             this.refresh();
37278         }else{
37279             if(!isUpdate){
37280                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37281             }
37282             var s = this.getScrollState();
37283             var markup = this.renderRows(firstRow, lastRow);
37284             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37285             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37286             this.restoreScroll(s);
37287             if(!isUpdate){
37288                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37289                 this.syncRowHeights(firstRow, lastRow);
37290                 this.stripeRows(firstRow);
37291                 this.layout();
37292             }
37293         }
37294     },
37295
37296     bufferRows : function(markup, target, index){
37297         var before = null, trows = target.rows, tbody = target.tBodies[0];
37298         if(index < trows.length){
37299             before = trows[index];
37300         }
37301         var b = document.createElement("div");
37302         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37303         var rows = b.firstChild.rows;
37304         for(var i = 0, len = rows.length; i < len; i++){
37305             if(before){
37306                 tbody.insertBefore(rows[0], before);
37307             }else{
37308                 tbody.appendChild(rows[0]);
37309             }
37310         }
37311         b.innerHTML = "";
37312         b = null;
37313     },
37314
37315     deleteRows : function(dm, firstRow, lastRow){
37316         if(dm.getRowCount()<1){
37317             this.fireEvent("beforerefresh", this);
37318             this.mainBody.update("");
37319             this.lockedBody.update("");
37320             this.fireEvent("refresh", this);
37321         }else{
37322             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37323             var bt = this.getBodyTable();
37324             var tbody = bt.firstChild;
37325             var rows = bt.rows;
37326             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37327                 tbody.removeChild(rows[firstRow]);
37328             }
37329             this.stripeRows(firstRow);
37330             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37331         }
37332     },
37333
37334     updateRows : function(dataSource, firstRow, lastRow){
37335         var s = this.getScrollState();
37336         this.refresh();
37337         this.restoreScroll(s);
37338     },
37339
37340     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37341         if(!noRefresh){
37342            this.refresh();
37343         }
37344         this.updateHeaderSortState();
37345     },
37346
37347     getScrollState : function(){
37348         
37349         var sb = this.scroller.dom;
37350         return {left: sb.scrollLeft, top: sb.scrollTop};
37351     },
37352
37353     stripeRows : function(startRow){
37354         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37355             return;
37356         }
37357         startRow = startRow || 0;
37358         var rows = this.getBodyTable().rows;
37359         var lrows = this.getLockedTable().rows;
37360         var cls = ' x-grid-row-alt ';
37361         for(var i = startRow, len = rows.length; i < len; i++){
37362             var row = rows[i], lrow = lrows[i];
37363             var isAlt = ((i+1) % 2 == 0);
37364             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37365             if(isAlt == hasAlt){
37366                 continue;
37367             }
37368             if(isAlt){
37369                 row.className += " x-grid-row-alt";
37370             }else{
37371                 row.className = row.className.replace("x-grid-row-alt", "");
37372             }
37373             if(lrow){
37374                 lrow.className = row.className;
37375             }
37376         }
37377     },
37378
37379     restoreScroll : function(state){
37380         //Roo.log('GridView.restoreScroll');
37381         var sb = this.scroller.dom;
37382         sb.scrollLeft = state.left;
37383         sb.scrollTop = state.top;
37384         this.syncScroll();
37385     },
37386
37387     syncScroll : function(){
37388         //Roo.log('GridView.syncScroll');
37389         var sb = this.scroller.dom;
37390         var sh = this.mainHd.dom;
37391         var bs = this.mainBody.dom;
37392         var lv = this.lockedBody.dom;
37393         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37394         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37395     },
37396
37397     handleScroll : function(e){
37398         this.syncScroll();
37399         var sb = this.scroller.dom;
37400         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37401         e.stopEvent();
37402     },
37403
37404     handleWheel : function(e){
37405         var d = e.getWheelDelta();
37406         this.scroller.dom.scrollTop -= d*22;
37407         // set this here to prevent jumpy scrolling on large tables
37408         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37409         e.stopEvent();
37410     },
37411
37412     renderRows : function(startRow, endRow){
37413         // pull in all the crap needed to render rows
37414         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37415         var colCount = cm.getColumnCount();
37416
37417         if(ds.getCount() < 1){
37418             return ["", ""];
37419         }
37420
37421         // build a map for all the columns
37422         var cs = [];
37423         for(var i = 0; i < colCount; i++){
37424             var name = cm.getDataIndex(i);
37425             cs[i] = {
37426                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37427                 renderer : cm.getRenderer(i),
37428                 id : cm.getColumnId(i),
37429                 locked : cm.isLocked(i)
37430             };
37431         }
37432
37433         startRow = startRow || 0;
37434         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37435
37436         // records to render
37437         var rs = ds.getRange(startRow, endRow);
37438
37439         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37440     },
37441
37442     // As much as I hate to duplicate code, this was branched because FireFox really hates
37443     // [].join("") on strings. The performance difference was substantial enough to
37444     // branch this function
37445     doRender : Roo.isGecko ?
37446             function(cs, rs, ds, startRow, colCount, stripe){
37447                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37448                 // buffers
37449                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37450                 
37451                 var hasListener = this.grid.hasListener('rowclass');
37452                 var rowcfg = {};
37453                 for(var j = 0, len = rs.length; j < len; j++){
37454                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37455                     for(var i = 0; i < colCount; i++){
37456                         c = cs[i];
37457                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37458                         p.id = c.id;
37459                         p.css = p.attr = "";
37460                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37461                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37462                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37463                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37464                         }
37465                         var markup = ct.apply(p);
37466                         if(!c.locked){
37467                             cb+= markup;
37468                         }else{
37469                             lcb+= markup;
37470                         }
37471                     }
37472                     var alt = [];
37473                     if(stripe && ((rowIndex+1) % 2 == 0)){
37474                         alt.push("x-grid-row-alt")
37475                     }
37476                     if(r.dirty){
37477                         alt.push(  " x-grid-dirty-row");
37478                     }
37479                     rp.cells = lcb;
37480                     if(this.getRowClass){
37481                         alt.push(this.getRowClass(r, rowIndex));
37482                     }
37483                     if (hasListener) {
37484                         rowcfg = {
37485                              
37486                             record: r,
37487                             rowIndex : rowIndex,
37488                             rowClass : ''
37489                         }
37490                         this.grid.fireEvent('rowclass', this, rowcfg);
37491                         alt.push(rowcfg.rowClass);
37492                     }
37493                     rp.alt = alt.join(" ");
37494                     lbuf+= rt.apply(rp);
37495                     rp.cells = cb;
37496                     buf+=  rt.apply(rp);
37497                 }
37498                 return [lbuf, buf];
37499             } :
37500             function(cs, rs, ds, startRow, colCount, stripe){
37501                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37502                 // buffers
37503                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37504                 var hasListener = this.grid.hasListener('rowclass');
37505  
37506                 var rowcfg = {};
37507                 for(var j = 0, len = rs.length; j < len; j++){
37508                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37509                     for(var i = 0; i < colCount; i++){
37510                         c = cs[i];
37511                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37512                         p.id = c.id;
37513                         p.css = p.attr = "";
37514                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37515                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37516                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37517                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37518                         }
37519                         
37520                         var markup = ct.apply(p);
37521                         if(!c.locked){
37522                             cb[cb.length] = markup;
37523                         }else{
37524                             lcb[lcb.length] = markup;
37525                         }
37526                     }
37527                     var alt = [];
37528                     if(stripe && ((rowIndex+1) % 2 == 0)){
37529                         alt.push( "x-grid-row-alt");
37530                     }
37531                     if(r.dirty){
37532                         alt.push(" x-grid-dirty-row");
37533                     }
37534                     rp.cells = lcb;
37535                     if(this.getRowClass){
37536                         alt.push( this.getRowClass(r, rowIndex));
37537                     }
37538                     if (hasListener) {
37539                         rowcfg = {
37540                              
37541                             record: r,
37542                             rowIndex : rowIndex,
37543                             rowClass : ''
37544                         }
37545                         this.grid.fireEvent('rowclass', this, rowcfg);
37546                         alt.push(rowcfg.rowClass);
37547                     }
37548                     rp.alt = alt.join(" ");
37549                     rp.cells = lcb.join("");
37550                     lbuf[lbuf.length] = rt.apply(rp);
37551                     rp.cells = cb.join("");
37552                     buf[buf.length] =  rt.apply(rp);
37553                 }
37554                 return [lbuf.join(""), buf.join("")];
37555             },
37556
37557     renderBody : function(){
37558         var markup = this.renderRows();
37559         var bt = this.templates.body;
37560         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37561     },
37562
37563     /**
37564      * Refreshes the grid
37565      * @param {Boolean} headersToo
37566      */
37567     refresh : function(headersToo){
37568         this.fireEvent("beforerefresh", this);
37569         this.grid.stopEditing();
37570         var result = this.renderBody();
37571         this.lockedBody.update(result[0]);
37572         this.mainBody.update(result[1]);
37573         if(headersToo === true){
37574             this.updateHeaders();
37575             this.updateColumns();
37576             this.updateSplitters();
37577             this.updateHeaderSortState();
37578         }
37579         this.syncRowHeights();
37580         this.layout();
37581         this.fireEvent("refresh", this);
37582     },
37583
37584     handleColumnMove : function(cm, oldIndex, newIndex){
37585         this.indexMap = null;
37586         var s = this.getScrollState();
37587         this.refresh(true);
37588         this.restoreScroll(s);
37589         this.afterMove(newIndex);
37590     },
37591
37592     afterMove : function(colIndex){
37593         if(this.enableMoveAnim && Roo.enableFx){
37594             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37595         }
37596         // if multisort - fix sortOrder, and reload..
37597         if (this.grid.dataSource.multiSort) {
37598             // the we can call sort again..
37599             var dm = this.grid.dataSource;
37600             var cm = this.grid.colModel;
37601             var so = [];
37602             for(var i = 0; i < cm.config.length; i++ ) {
37603                 
37604                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37605                     continue; // dont' bother, it's not in sort list or being set.
37606                 }
37607                 
37608                 so.push(cm.config[i].dataIndex);
37609             };
37610             dm.sortOrder = so;
37611             dm.load(dm.lastOptions);
37612             
37613             
37614         }
37615         
37616     },
37617
37618     updateCell : function(dm, rowIndex, dataIndex){
37619         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37620         if(typeof colIndex == "undefined"){ // not present in grid
37621             return;
37622         }
37623         var cm = this.grid.colModel;
37624         var cell = this.getCell(rowIndex, colIndex);
37625         var cellText = this.getCellText(rowIndex, colIndex);
37626
37627         var p = {
37628             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37629             id : cm.getColumnId(colIndex),
37630             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37631         };
37632         var renderer = cm.getRenderer(colIndex);
37633         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37634         if(typeof val == "undefined" || val === "") val = "&#160;";
37635         cellText.innerHTML = val;
37636         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37637         this.syncRowHeights(rowIndex, rowIndex);
37638     },
37639
37640     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37641         var maxWidth = 0;
37642         if(this.grid.autoSizeHeaders){
37643             var h = this.getHeaderCellMeasure(colIndex);
37644             maxWidth = Math.max(maxWidth, h.scrollWidth);
37645         }
37646         var tb, index;
37647         if(this.cm.isLocked(colIndex)){
37648             tb = this.getLockedTable();
37649             index = colIndex;
37650         }else{
37651             tb = this.getBodyTable();
37652             index = colIndex - this.cm.getLockedCount();
37653         }
37654         if(tb && tb.rows){
37655             var rows = tb.rows;
37656             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37657             for(var i = 0; i < stopIndex; i++){
37658                 var cell = rows[i].childNodes[index].firstChild;
37659                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37660             }
37661         }
37662         return maxWidth + /*margin for error in IE*/ 5;
37663     },
37664     /**
37665      * Autofit a column to its content.
37666      * @param {Number} colIndex
37667      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37668      */
37669      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37670          if(this.cm.isHidden(colIndex)){
37671              return; // can't calc a hidden column
37672          }
37673         if(forceMinSize){
37674             var cid = this.cm.getColumnId(colIndex);
37675             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37676            if(this.grid.autoSizeHeaders){
37677                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37678            }
37679         }
37680         var newWidth = this.calcColumnWidth(colIndex);
37681         this.cm.setColumnWidth(colIndex,
37682             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37683         if(!suppressEvent){
37684             this.grid.fireEvent("columnresize", colIndex, newWidth);
37685         }
37686     },
37687
37688     /**
37689      * Autofits all columns to their content and then expands to fit any extra space in the grid
37690      */
37691      autoSizeColumns : function(){
37692         var cm = this.grid.colModel;
37693         var colCount = cm.getColumnCount();
37694         for(var i = 0; i < colCount; i++){
37695             this.autoSizeColumn(i, true, true);
37696         }
37697         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37698             this.fitColumns();
37699         }else{
37700             this.updateColumns();
37701             this.layout();
37702         }
37703     },
37704
37705     /**
37706      * Autofits all columns to the grid's width proportionate with their current size
37707      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37708      */
37709     fitColumns : function(reserveScrollSpace){
37710         var cm = this.grid.colModel;
37711         var colCount = cm.getColumnCount();
37712         var cols = [];
37713         var width = 0;
37714         var i, w;
37715         for (i = 0; i < colCount; i++){
37716             if(!cm.isHidden(i) && !cm.isFixed(i)){
37717                 w = cm.getColumnWidth(i);
37718                 cols.push(i);
37719                 cols.push(w);
37720                 width += w;
37721             }
37722         }
37723         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37724         if(reserveScrollSpace){
37725             avail -= 17;
37726         }
37727         var frac = (avail - cm.getTotalWidth())/width;
37728         while (cols.length){
37729             w = cols.pop();
37730             i = cols.pop();
37731             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37732         }
37733         this.updateColumns();
37734         this.layout();
37735     },
37736
37737     onRowSelect : function(rowIndex){
37738         var row = this.getRowComposite(rowIndex);
37739         row.addClass("x-grid-row-selected");
37740     },
37741
37742     onRowDeselect : function(rowIndex){
37743         var row = this.getRowComposite(rowIndex);
37744         row.removeClass("x-grid-row-selected");
37745     },
37746
37747     onCellSelect : function(row, col){
37748         var cell = this.getCell(row, col);
37749         if(cell){
37750             Roo.fly(cell).addClass("x-grid-cell-selected");
37751         }
37752     },
37753
37754     onCellDeselect : function(row, col){
37755         var cell = this.getCell(row, col);
37756         if(cell){
37757             Roo.fly(cell).removeClass("x-grid-cell-selected");
37758         }
37759     },
37760
37761     updateHeaderSortState : function(){
37762         
37763         // sort state can be single { field: xxx, direction : yyy}
37764         // or   { xxx=>ASC , yyy : DESC ..... }
37765         
37766         var mstate = {};
37767         if (!this.ds.multiSort) { 
37768             var state = this.ds.getSortState();
37769             if(!state){
37770                 return;
37771             }
37772             mstate[state.field] = state.direction;
37773             // FIXME... - this is not used here.. but might be elsewhere..
37774             this.sortState = state;
37775             
37776         } else {
37777             mstate = this.ds.sortToggle;
37778         }
37779         //remove existing sort classes..
37780         
37781         var sc = this.sortClasses;
37782         var hds = this.el.select(this.headerSelector).removeClass(sc);
37783         
37784         for(var f in mstate) {
37785         
37786             var sortColumn = this.cm.findColumnIndex(f);
37787             
37788             if(sortColumn != -1){
37789                 var sortDir = mstate[f];        
37790                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37791             }
37792         }
37793         
37794          
37795         
37796     },
37797
37798
37799     handleHeaderClick : function(g, index,e){
37800         
37801         Roo.log("header click");
37802         
37803         if (Roo.isTouch) {
37804             // touch events on header are handled by context
37805             this.handleHdCtx(g,index,e);
37806             return;
37807         }
37808         
37809         
37810         if(this.headersDisabled){
37811             return;
37812         }
37813         var dm = g.dataSource, cm = g.colModel;
37814         if(!cm.isSortable(index)){
37815             return;
37816         }
37817         g.stopEditing();
37818         
37819         if (dm.multiSort) {
37820             // update the sortOrder
37821             var so = [];
37822             for(var i = 0; i < cm.config.length; i++ ) {
37823                 
37824                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37825                     continue; // dont' bother, it's not in sort list or being set.
37826                 }
37827                 
37828                 so.push(cm.config[i].dataIndex);
37829             };
37830             dm.sortOrder = so;
37831         }
37832         
37833         
37834         dm.sort(cm.getDataIndex(index));
37835     },
37836
37837
37838     destroy : function(){
37839         if(this.colMenu){
37840             this.colMenu.removeAll();
37841             Roo.menu.MenuMgr.unregister(this.colMenu);
37842             this.colMenu.getEl().remove();
37843             delete this.colMenu;
37844         }
37845         if(this.hmenu){
37846             this.hmenu.removeAll();
37847             Roo.menu.MenuMgr.unregister(this.hmenu);
37848             this.hmenu.getEl().remove();
37849             delete this.hmenu;
37850         }
37851         if(this.grid.enableColumnMove){
37852             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37853             if(dds){
37854                 for(var dd in dds){
37855                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37856                         var elid = dds[dd].dragElId;
37857                         dds[dd].unreg();
37858                         Roo.get(elid).remove();
37859                     } else if(dds[dd].config.isTarget){
37860                         dds[dd].proxyTop.remove();
37861                         dds[dd].proxyBottom.remove();
37862                         dds[dd].unreg();
37863                     }
37864                     if(Roo.dd.DDM.locationCache[dd]){
37865                         delete Roo.dd.DDM.locationCache[dd];
37866                     }
37867                 }
37868                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37869             }
37870         }
37871         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37872         this.bind(null, null);
37873         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37874     },
37875
37876     handleLockChange : function(){
37877         this.refresh(true);
37878     },
37879
37880     onDenyColumnLock : function(){
37881
37882     },
37883
37884     onDenyColumnHide : function(){
37885
37886     },
37887
37888     handleHdMenuClick : function(item){
37889         var index = this.hdCtxIndex;
37890         var cm = this.cm, ds = this.ds;
37891         switch(item.id){
37892             case "asc":
37893                 ds.sort(cm.getDataIndex(index), "ASC");
37894                 break;
37895             case "desc":
37896                 ds.sort(cm.getDataIndex(index), "DESC");
37897                 break;
37898             case "lock":
37899                 var lc = cm.getLockedCount();
37900                 if(cm.getColumnCount(true) <= lc+1){
37901                     this.onDenyColumnLock();
37902                     return;
37903                 }
37904                 if(lc != index){
37905                     cm.setLocked(index, true, true);
37906                     cm.moveColumn(index, lc);
37907                     this.grid.fireEvent("columnmove", index, lc);
37908                 }else{
37909                     cm.setLocked(index, true);
37910                 }
37911             break;
37912             case "unlock":
37913                 var lc = cm.getLockedCount();
37914                 if((lc-1) != index){
37915                     cm.setLocked(index, false, true);
37916                     cm.moveColumn(index, lc-1);
37917                     this.grid.fireEvent("columnmove", index, lc-1);
37918                 }else{
37919                     cm.setLocked(index, false);
37920                 }
37921             break;
37922             case 'wider': // used to expand cols on touch..
37923             case 'narrow':
37924                 var cw = cm.getColumnWidth(index);
37925                 cw += (item.id == 'wider' ? 1 : -1) * 50;
37926                 cw = Math.max(0, cw);
37927                 cw = Math.min(cw,4000);
37928                 cm.setColumnWidth(index, cw);
37929                 break;
37930                 
37931             default:
37932                 index = cm.getIndexById(item.id.substr(4));
37933                 if(index != -1){
37934                     if(item.checked && cm.getColumnCount(true) <= 1){
37935                         this.onDenyColumnHide();
37936                         return false;
37937                     }
37938                     cm.setHidden(index, item.checked);
37939                 }
37940         }
37941         return true;
37942     },
37943
37944     beforeColMenuShow : function(){
37945         var cm = this.cm,  colCount = cm.getColumnCount();
37946         this.colMenu.removeAll();
37947         for(var i = 0; i < colCount; i++){
37948             this.colMenu.add(new Roo.menu.CheckItem({
37949                 id: "col-"+cm.getColumnId(i),
37950                 text: cm.getColumnHeader(i),
37951                 checked: !cm.isHidden(i),
37952                 hideOnClick:false
37953             }));
37954         }
37955     },
37956
37957     handleHdCtx : function(g, index, e){
37958         e.stopEvent();
37959         var hd = this.getHeaderCell(index);
37960         this.hdCtxIndex = index;
37961         var ms = this.hmenu.items, cm = this.cm;
37962         ms.get("asc").setDisabled(!cm.isSortable(index));
37963         ms.get("desc").setDisabled(!cm.isSortable(index));
37964         if(this.grid.enableColLock !== false){
37965             ms.get("lock").setDisabled(cm.isLocked(index));
37966             ms.get("unlock").setDisabled(!cm.isLocked(index));
37967         }
37968         this.hmenu.show(hd, "tl-bl");
37969     },
37970
37971     handleHdOver : function(e){
37972         var hd = this.findHeaderCell(e.getTarget());
37973         if(hd && !this.headersDisabled){
37974             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37975                this.fly(hd).addClass("x-grid-hd-over");
37976             }
37977         }
37978     },
37979
37980     handleHdOut : function(e){
37981         var hd = this.findHeaderCell(e.getTarget());
37982         if(hd){
37983             this.fly(hd).removeClass("x-grid-hd-over");
37984         }
37985     },
37986
37987     handleSplitDblClick : function(e, t){
37988         var i = this.getCellIndex(t);
37989         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37990             this.autoSizeColumn(i, true);
37991             this.layout();
37992         }
37993     },
37994
37995     render : function(){
37996
37997         var cm = this.cm;
37998         var colCount = cm.getColumnCount();
37999
38000         if(this.grid.monitorWindowResize === true){
38001             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38002         }
38003         var header = this.renderHeaders();
38004         var body = this.templates.body.apply({rows:""});
38005         var html = this.templates.master.apply({
38006             lockedBody: body,
38007             body: body,
38008             lockedHeader: header[0],
38009             header: header[1]
38010         });
38011
38012         //this.updateColumns();
38013
38014         this.grid.getGridEl().dom.innerHTML = html;
38015
38016         this.initElements();
38017         
38018         // a kludge to fix the random scolling effect in webkit
38019         this.el.on("scroll", function() {
38020             this.el.dom.scrollTop=0; // hopefully not recursive..
38021         },this);
38022
38023         this.scroller.on("scroll", this.handleScroll, this);
38024         this.lockedBody.on("mousewheel", this.handleWheel, this);
38025         this.mainBody.on("mousewheel", this.handleWheel, this);
38026
38027         this.mainHd.on("mouseover", this.handleHdOver, this);
38028         this.mainHd.on("mouseout", this.handleHdOut, this);
38029         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38030                 {delegate: "."+this.splitClass});
38031
38032         this.lockedHd.on("mouseover", this.handleHdOver, this);
38033         this.lockedHd.on("mouseout", this.handleHdOut, this);
38034         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38035                 {delegate: "."+this.splitClass});
38036
38037         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38038             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38039         }
38040
38041         this.updateSplitters();
38042
38043         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38044             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38045             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38046         }
38047
38048         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38049             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38050             this.hmenu.add(
38051                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38052                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38053             );
38054             if(this.grid.enableColLock !== false){
38055                 this.hmenu.add('-',
38056                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38057                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38058                 );
38059             }
38060             if (Roo.isTouch) {
38061                  this.hmenu.add('-',
38062                     {id:"wider", text: this.columnsWiderText},
38063                     {id:"narrow", text: this.columnsNarrowText }
38064                 );
38065                 
38066                  
38067             }
38068             
38069             if(this.grid.enableColumnHide !== false){
38070
38071                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38072                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38073                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38074
38075                 this.hmenu.add('-',
38076                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38077                 );
38078             }
38079             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38080
38081             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38082         }
38083
38084         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38085             this.dd = new Roo.grid.GridDragZone(this.grid, {
38086                 ddGroup : this.grid.ddGroup || 'GridDD'
38087             });
38088             
38089         }
38090
38091         /*
38092         for(var i = 0; i < colCount; i++){
38093             if(cm.isHidden(i)){
38094                 this.hideColumn(i);
38095             }
38096             if(cm.config[i].align){
38097                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38098                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38099             }
38100         }*/
38101         
38102         this.updateHeaderSortState();
38103
38104         this.beforeInitialResize();
38105         this.layout(true);
38106
38107         // two part rendering gives faster view to the user
38108         this.renderPhase2.defer(1, this);
38109     },
38110
38111     renderPhase2 : function(){
38112         // render the rows now
38113         this.refresh();
38114         if(this.grid.autoSizeColumns){
38115             this.autoSizeColumns();
38116         }
38117     },
38118
38119     beforeInitialResize : function(){
38120
38121     },
38122
38123     onColumnSplitterMoved : function(i, w){
38124         this.userResized = true;
38125         var cm = this.grid.colModel;
38126         cm.setColumnWidth(i, w, true);
38127         var cid = cm.getColumnId(i);
38128         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38129         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38130         this.updateSplitters();
38131         this.layout();
38132         this.grid.fireEvent("columnresize", i, w);
38133     },
38134
38135     syncRowHeights : function(startIndex, endIndex){
38136         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38137             startIndex = startIndex || 0;
38138             var mrows = this.getBodyTable().rows;
38139             var lrows = this.getLockedTable().rows;
38140             var len = mrows.length-1;
38141             endIndex = Math.min(endIndex || len, len);
38142             for(var i = startIndex; i <= endIndex; i++){
38143                 var m = mrows[i], l = lrows[i];
38144                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38145                 m.style.height = l.style.height = h + "px";
38146             }
38147         }
38148     },
38149
38150     layout : function(initialRender, is2ndPass){
38151         var g = this.grid;
38152         var auto = g.autoHeight;
38153         var scrollOffset = 16;
38154         var c = g.getGridEl(), cm = this.cm,
38155                 expandCol = g.autoExpandColumn,
38156                 gv = this;
38157         //c.beginMeasure();
38158
38159         if(!c.dom.offsetWidth){ // display:none?
38160             if(initialRender){
38161                 this.lockedWrap.show();
38162                 this.mainWrap.show();
38163             }
38164             return;
38165         }
38166
38167         var hasLock = this.cm.isLocked(0);
38168
38169         var tbh = this.headerPanel.getHeight();
38170         var bbh = this.footerPanel.getHeight();
38171
38172         if(auto){
38173             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38174             var newHeight = ch + c.getBorderWidth("tb");
38175             if(g.maxHeight){
38176                 newHeight = Math.min(g.maxHeight, newHeight);
38177             }
38178             c.setHeight(newHeight);
38179         }
38180
38181         if(g.autoWidth){
38182             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38183         }
38184
38185         var s = this.scroller;
38186
38187         var csize = c.getSize(true);
38188
38189         this.el.setSize(csize.width, csize.height);
38190
38191         this.headerPanel.setWidth(csize.width);
38192         this.footerPanel.setWidth(csize.width);
38193
38194         var hdHeight = this.mainHd.getHeight();
38195         var vw = csize.width;
38196         var vh = csize.height - (tbh + bbh);
38197
38198         s.setSize(vw, vh);
38199
38200         var bt = this.getBodyTable();
38201         var ltWidth = hasLock ?
38202                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38203
38204         var scrollHeight = bt.offsetHeight;
38205         var scrollWidth = ltWidth + bt.offsetWidth;
38206         var vscroll = false, hscroll = false;
38207
38208         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38209
38210         var lw = this.lockedWrap, mw = this.mainWrap;
38211         var lb = this.lockedBody, mb = this.mainBody;
38212
38213         setTimeout(function(){
38214             var t = s.dom.offsetTop;
38215             var w = s.dom.clientWidth,
38216                 h = s.dom.clientHeight;
38217
38218             lw.setTop(t);
38219             lw.setSize(ltWidth, h);
38220
38221             mw.setLeftTop(ltWidth, t);
38222             mw.setSize(w-ltWidth, h);
38223
38224             lb.setHeight(h-hdHeight);
38225             mb.setHeight(h-hdHeight);
38226
38227             if(is2ndPass !== true && !gv.userResized && expandCol){
38228                 // high speed resize without full column calculation
38229                 
38230                 var ci = cm.getIndexById(expandCol);
38231                 if (ci < 0) {
38232                     ci = cm.findColumnIndex(expandCol);
38233                 }
38234                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38235                 var expandId = cm.getColumnId(ci);
38236                 var  tw = cm.getTotalWidth(false);
38237                 var currentWidth = cm.getColumnWidth(ci);
38238                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38239                 if(currentWidth != cw){
38240                     cm.setColumnWidth(ci, cw, true);
38241                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38242                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38243                     gv.updateSplitters();
38244                     gv.layout(false, true);
38245                 }
38246             }
38247
38248             if(initialRender){
38249                 lw.show();
38250                 mw.show();
38251             }
38252             //c.endMeasure();
38253         }, 10);
38254     },
38255
38256     onWindowResize : function(){
38257         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38258             return;
38259         }
38260         this.layout();
38261     },
38262
38263     appendFooter : function(parentEl){
38264         return null;
38265     },
38266
38267     sortAscText : "Sort Ascending",
38268     sortDescText : "Sort Descending",
38269     lockText : "Lock Column",
38270     unlockText : "Unlock Column",
38271     columnsText : "Columns",
38272  
38273     columnsWiderText : "Wider",
38274     columnsNarrowText : "Thinner"
38275 });
38276
38277
38278 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38279     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38280     this.proxy.el.addClass('x-grid3-col-dd');
38281 };
38282
38283 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38284     handleMouseDown : function(e){
38285
38286     },
38287
38288     callHandleMouseDown : function(e){
38289         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38290     }
38291 });
38292 /*
38293  * Based on:
38294  * Ext JS Library 1.1.1
38295  * Copyright(c) 2006-2007, Ext JS, LLC.
38296  *
38297  * Originally Released Under LGPL - original licence link has changed is not relivant.
38298  *
38299  * Fork - LGPL
38300  * <script type="text/javascript">
38301  */
38302  
38303 // private
38304 // This is a support class used internally by the Grid components
38305 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38306     this.grid = grid;
38307     this.view = grid.getView();
38308     this.proxy = this.view.resizeProxy;
38309     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38310         "gridSplitters" + this.grid.getGridEl().id, {
38311         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38312     });
38313     this.setHandleElId(Roo.id(hd));
38314     this.setOuterHandleElId(Roo.id(hd2));
38315     this.scroll = false;
38316 };
38317 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38318     fly: Roo.Element.fly,
38319
38320     b4StartDrag : function(x, y){
38321         this.view.headersDisabled = true;
38322         this.proxy.setHeight(this.view.mainWrap.getHeight());
38323         var w = this.cm.getColumnWidth(this.cellIndex);
38324         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38325         this.resetConstraints();
38326         this.setXConstraint(minw, 1000);
38327         this.setYConstraint(0, 0);
38328         this.minX = x - minw;
38329         this.maxX = x + 1000;
38330         this.startPos = x;
38331         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38332     },
38333
38334
38335     handleMouseDown : function(e){
38336         ev = Roo.EventObject.setEvent(e);
38337         var t = this.fly(ev.getTarget());
38338         if(t.hasClass("x-grid-split")){
38339             this.cellIndex = this.view.getCellIndex(t.dom);
38340             this.split = t.dom;
38341             this.cm = this.grid.colModel;
38342             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38343                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38344             }
38345         }
38346     },
38347
38348     endDrag : function(e){
38349         this.view.headersDisabled = false;
38350         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38351         var diff = endX - this.startPos;
38352         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38353     },
38354
38355     autoOffset : function(){
38356         this.setDelta(0,0);
38357     }
38358 });/*
38359  * Based on:
38360  * Ext JS Library 1.1.1
38361  * Copyright(c) 2006-2007, Ext JS, LLC.
38362  *
38363  * Originally Released Under LGPL - original licence link has changed is not relivant.
38364  *
38365  * Fork - LGPL
38366  * <script type="text/javascript">
38367  */
38368  
38369 // private
38370 // This is a support class used internally by the Grid components
38371 Roo.grid.GridDragZone = function(grid, config){
38372     this.view = grid.getView();
38373     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38374     if(this.view.lockedBody){
38375         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38376         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38377     }
38378     this.scroll = false;
38379     this.grid = grid;
38380     this.ddel = document.createElement('div');
38381     this.ddel.className = 'x-grid-dd-wrap';
38382 };
38383
38384 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38385     ddGroup : "GridDD",
38386
38387     getDragData : function(e){
38388         var t = Roo.lib.Event.getTarget(e);
38389         var rowIndex = this.view.findRowIndex(t);
38390         var sm = this.grid.selModel;
38391             
38392         //Roo.log(rowIndex);
38393         
38394         if (sm.getSelectedCell) {
38395             // cell selection..
38396             if (!sm.getSelectedCell()) {
38397                 return false;
38398             }
38399             if (rowIndex != sm.getSelectedCell()[0]) {
38400                 return false;
38401             }
38402         
38403         }
38404         
38405         if(rowIndex !== false){
38406             
38407             // if editorgrid.. 
38408             
38409             
38410             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38411                
38412             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38413               //  
38414             //}
38415             if (e.hasModifier()){
38416                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38417             }
38418             
38419             Roo.log("getDragData");
38420             
38421             return {
38422                 grid: this.grid,
38423                 ddel: this.ddel,
38424                 rowIndex: rowIndex,
38425                 selections:sm.getSelections ? sm.getSelections() : (
38426                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38427                 )
38428             };
38429         }
38430         return false;
38431     },
38432
38433     onInitDrag : function(e){
38434         var data = this.dragData;
38435         this.ddel.innerHTML = this.grid.getDragDropText();
38436         this.proxy.update(this.ddel);
38437         // fire start drag?
38438     },
38439
38440     afterRepair : function(){
38441         this.dragging = false;
38442     },
38443
38444     getRepairXY : function(e, data){
38445         return false;
38446     },
38447
38448     onEndDrag : function(data, e){
38449         // fire end drag?
38450     },
38451
38452     onValidDrop : function(dd, e, id){
38453         // fire drag drop?
38454         this.hideProxy();
38455     },
38456
38457     beforeInvalidDrop : function(e, id){
38458
38459     }
38460 });/*
38461  * Based on:
38462  * Ext JS Library 1.1.1
38463  * Copyright(c) 2006-2007, Ext JS, LLC.
38464  *
38465  * Originally Released Under LGPL - original licence link has changed is not relivant.
38466  *
38467  * Fork - LGPL
38468  * <script type="text/javascript">
38469  */
38470  
38471
38472 /**
38473  * @class Roo.grid.ColumnModel
38474  * @extends Roo.util.Observable
38475  * This is the default implementation of a ColumnModel used by the Grid. It defines
38476  * the columns in the grid.
38477  * <br>Usage:<br>
38478  <pre><code>
38479  var colModel = new Roo.grid.ColumnModel([
38480         {header: "Ticker", width: 60, sortable: true, locked: true},
38481         {header: "Company Name", width: 150, sortable: true},
38482         {header: "Market Cap.", width: 100, sortable: true},
38483         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38484         {header: "Employees", width: 100, sortable: true, resizable: false}
38485  ]);
38486  </code></pre>
38487  * <p>
38488  
38489  * The config options listed for this class are options which may appear in each
38490  * individual column definition.
38491  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38492  * @constructor
38493  * @param {Object} config An Array of column config objects. See this class's
38494  * config objects for details.
38495 */
38496 Roo.grid.ColumnModel = function(config){
38497         /**
38498      * The config passed into the constructor
38499      */
38500     this.config = config;
38501     this.lookup = {};
38502
38503     // if no id, create one
38504     // if the column does not have a dataIndex mapping,
38505     // map it to the order it is in the config
38506     for(var i = 0, len = config.length; i < len; i++){
38507         var c = config[i];
38508         if(typeof c.dataIndex == "undefined"){
38509             c.dataIndex = i;
38510         }
38511         if(typeof c.renderer == "string"){
38512             c.renderer = Roo.util.Format[c.renderer];
38513         }
38514         if(typeof c.id == "undefined"){
38515             c.id = Roo.id();
38516         }
38517         if(c.editor && c.editor.xtype){
38518             c.editor  = Roo.factory(c.editor, Roo.grid);
38519         }
38520         if(c.editor && c.editor.isFormField){
38521             c.editor = new Roo.grid.GridEditor(c.editor);
38522         }
38523         this.lookup[c.id] = c;
38524     }
38525
38526     /**
38527      * The width of columns which have no width specified (defaults to 100)
38528      * @type Number
38529      */
38530     this.defaultWidth = 100;
38531
38532     /**
38533      * Default sortable of columns which have no sortable specified (defaults to false)
38534      * @type Boolean
38535      */
38536     this.defaultSortable = false;
38537
38538     this.addEvents({
38539         /**
38540              * @event widthchange
38541              * Fires when the width of a column changes.
38542              * @param {ColumnModel} this
38543              * @param {Number} columnIndex The column index
38544              * @param {Number} newWidth The new width
38545              */
38546             "widthchange": true,
38547         /**
38548              * @event headerchange
38549              * Fires when the text of a header changes.
38550              * @param {ColumnModel} this
38551              * @param {Number} columnIndex The column index
38552              * @param {Number} newText The new header text
38553              */
38554             "headerchange": true,
38555         /**
38556              * @event hiddenchange
38557              * Fires when a column is hidden or "unhidden".
38558              * @param {ColumnModel} this
38559              * @param {Number} columnIndex The column index
38560              * @param {Boolean} hidden true if hidden, false otherwise
38561              */
38562             "hiddenchange": true,
38563             /**
38564          * @event columnmoved
38565          * Fires when a column is moved.
38566          * @param {ColumnModel} this
38567          * @param {Number} oldIndex
38568          * @param {Number} newIndex
38569          */
38570         "columnmoved" : true,
38571         /**
38572          * @event columlockchange
38573          * Fires when a column's locked state is changed
38574          * @param {ColumnModel} this
38575          * @param {Number} colIndex
38576          * @param {Boolean} locked true if locked
38577          */
38578         "columnlockchange" : true
38579     });
38580     Roo.grid.ColumnModel.superclass.constructor.call(this);
38581 };
38582 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38583     /**
38584      * @cfg {String} header The header text to display in the Grid view.
38585      */
38586     /**
38587      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38588      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38589      * specified, the column's index is used as an index into the Record's data Array.
38590      */
38591     /**
38592      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38593      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38594      */
38595     /**
38596      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38597      * Defaults to the value of the {@link #defaultSortable} property.
38598      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38599      */
38600     /**
38601      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38602      */
38603     /**
38604      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38605      */
38606     /**
38607      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38608      */
38609     /**
38610      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38611      */
38612     /**
38613      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38614      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38615      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38616      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38617      */
38618        /**
38619      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38620      */
38621     /**
38622      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38623      */
38624     /**
38625      * @cfg {String} cursor (Optional)
38626      */
38627     /**
38628      * Returns the id of the column at the specified index.
38629      * @param {Number} index The column index
38630      * @return {String} the id
38631      */
38632     getColumnId : function(index){
38633         return this.config[index].id;
38634     },
38635
38636     /**
38637      * Returns the column for a specified id.
38638      * @param {String} id The column id
38639      * @return {Object} the column
38640      */
38641     getColumnById : function(id){
38642         return this.lookup[id];
38643     },
38644
38645     
38646     /**
38647      * Returns the column for a specified dataIndex.
38648      * @param {String} dataIndex The column dataIndex
38649      * @return {Object|Boolean} the column or false if not found
38650      */
38651     getColumnByDataIndex: function(dataIndex){
38652         var index = this.findColumnIndex(dataIndex);
38653         return index > -1 ? this.config[index] : false;
38654     },
38655     
38656     /**
38657      * Returns the index for a specified column id.
38658      * @param {String} id The column id
38659      * @return {Number} the index, or -1 if not found
38660      */
38661     getIndexById : function(id){
38662         for(var i = 0, len = this.config.length; i < len; i++){
38663             if(this.config[i].id == id){
38664                 return i;
38665             }
38666         }
38667         return -1;
38668     },
38669     
38670     /**
38671      * Returns the index for a specified column dataIndex.
38672      * @param {String} dataIndex The column dataIndex
38673      * @return {Number} the index, or -1 if not found
38674      */
38675     
38676     findColumnIndex : function(dataIndex){
38677         for(var i = 0, len = this.config.length; i < len; i++){
38678             if(this.config[i].dataIndex == dataIndex){
38679                 return i;
38680             }
38681         }
38682         return -1;
38683     },
38684     
38685     
38686     moveColumn : function(oldIndex, newIndex){
38687         var c = this.config[oldIndex];
38688         this.config.splice(oldIndex, 1);
38689         this.config.splice(newIndex, 0, c);
38690         this.dataMap = null;
38691         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38692     },
38693
38694     isLocked : function(colIndex){
38695         return this.config[colIndex].locked === true;
38696     },
38697
38698     setLocked : function(colIndex, value, suppressEvent){
38699         if(this.isLocked(colIndex) == value){
38700             return;
38701         }
38702         this.config[colIndex].locked = value;
38703         if(!suppressEvent){
38704             this.fireEvent("columnlockchange", this, colIndex, value);
38705         }
38706     },
38707
38708     getTotalLockedWidth : function(){
38709         var totalWidth = 0;
38710         for(var i = 0; i < this.config.length; i++){
38711             if(this.isLocked(i) && !this.isHidden(i)){
38712                 this.totalWidth += this.getColumnWidth(i);
38713             }
38714         }
38715         return totalWidth;
38716     },
38717
38718     getLockedCount : function(){
38719         for(var i = 0, len = this.config.length; i < len; i++){
38720             if(!this.isLocked(i)){
38721                 return i;
38722             }
38723         }
38724     },
38725
38726     /**
38727      * Returns the number of columns.
38728      * @return {Number}
38729      */
38730     getColumnCount : function(visibleOnly){
38731         if(visibleOnly === true){
38732             var c = 0;
38733             for(var i = 0, len = this.config.length; i < len; i++){
38734                 if(!this.isHidden(i)){
38735                     c++;
38736                 }
38737             }
38738             return c;
38739         }
38740         return this.config.length;
38741     },
38742
38743     /**
38744      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38745      * @param {Function} fn
38746      * @param {Object} scope (optional)
38747      * @return {Array} result
38748      */
38749     getColumnsBy : function(fn, scope){
38750         var r = [];
38751         for(var i = 0, len = this.config.length; i < len; i++){
38752             var c = this.config[i];
38753             if(fn.call(scope||this, c, i) === true){
38754                 r[r.length] = c;
38755             }
38756         }
38757         return r;
38758     },
38759
38760     /**
38761      * Returns true if the specified column is sortable.
38762      * @param {Number} col The column index
38763      * @return {Boolean}
38764      */
38765     isSortable : function(col){
38766         if(typeof this.config[col].sortable == "undefined"){
38767             return this.defaultSortable;
38768         }
38769         return this.config[col].sortable;
38770     },
38771
38772     /**
38773      * Returns the rendering (formatting) function defined for the column.
38774      * @param {Number} col The column index.
38775      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38776      */
38777     getRenderer : function(col){
38778         if(!this.config[col].renderer){
38779             return Roo.grid.ColumnModel.defaultRenderer;
38780         }
38781         return this.config[col].renderer;
38782     },
38783
38784     /**
38785      * Sets the rendering (formatting) function for a column.
38786      * @param {Number} col The column index
38787      * @param {Function} fn The function to use to process the cell's raw data
38788      * to return HTML markup for the grid view. The render function is called with
38789      * the following parameters:<ul>
38790      * <li>Data value.</li>
38791      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38792      * <li>css A CSS style string to apply to the table cell.</li>
38793      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38794      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38795      * <li>Row index</li>
38796      * <li>Column index</li>
38797      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38798      */
38799     setRenderer : function(col, fn){
38800         this.config[col].renderer = fn;
38801     },
38802
38803     /**
38804      * Returns the width for the specified column.
38805      * @param {Number} col The column index
38806      * @return {Number}
38807      */
38808     getColumnWidth : function(col){
38809         return this.config[col].width * 1 || this.defaultWidth;
38810     },
38811
38812     /**
38813      * Sets the width for a column.
38814      * @param {Number} col The column index
38815      * @param {Number} width The new width
38816      */
38817     setColumnWidth : function(col, width, suppressEvent){
38818         this.config[col].width = width;
38819         this.totalWidth = null;
38820         if(!suppressEvent){
38821              this.fireEvent("widthchange", this, col, width);
38822         }
38823     },
38824
38825     /**
38826      * Returns the total width of all columns.
38827      * @param {Boolean} includeHidden True to include hidden column widths
38828      * @return {Number}
38829      */
38830     getTotalWidth : function(includeHidden){
38831         if(!this.totalWidth){
38832             this.totalWidth = 0;
38833             for(var i = 0, len = this.config.length; i < len; i++){
38834                 if(includeHidden || !this.isHidden(i)){
38835                     this.totalWidth += this.getColumnWidth(i);
38836                 }
38837             }
38838         }
38839         return this.totalWidth;
38840     },
38841
38842     /**
38843      * Returns the header for the specified column.
38844      * @param {Number} col The column index
38845      * @return {String}
38846      */
38847     getColumnHeader : function(col){
38848         return this.config[col].header;
38849     },
38850
38851     /**
38852      * Sets the header for a column.
38853      * @param {Number} col The column index
38854      * @param {String} header The new header
38855      */
38856     setColumnHeader : function(col, header){
38857         this.config[col].header = header;
38858         this.fireEvent("headerchange", this, col, header);
38859     },
38860
38861     /**
38862      * Returns the tooltip for the specified column.
38863      * @param {Number} col The column index
38864      * @return {String}
38865      */
38866     getColumnTooltip : function(col){
38867             return this.config[col].tooltip;
38868     },
38869     /**
38870      * Sets the tooltip for a column.
38871      * @param {Number} col The column index
38872      * @param {String} tooltip The new tooltip
38873      */
38874     setColumnTooltip : function(col, tooltip){
38875             this.config[col].tooltip = tooltip;
38876     },
38877
38878     /**
38879      * Returns the dataIndex for the specified column.
38880      * @param {Number} col The column index
38881      * @return {Number}
38882      */
38883     getDataIndex : function(col){
38884         return this.config[col].dataIndex;
38885     },
38886
38887     /**
38888      * Sets the dataIndex for a column.
38889      * @param {Number} col The column index
38890      * @param {Number} dataIndex The new dataIndex
38891      */
38892     setDataIndex : function(col, dataIndex){
38893         this.config[col].dataIndex = dataIndex;
38894     },
38895
38896     
38897     
38898     /**
38899      * Returns true if the cell is editable.
38900      * @param {Number} colIndex The column index
38901      * @param {Number} rowIndex The row index
38902      * @return {Boolean}
38903      */
38904     isCellEditable : function(colIndex, rowIndex){
38905         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38906     },
38907
38908     /**
38909      * Returns the editor defined for the cell/column.
38910      * return false or null to disable editing.
38911      * @param {Number} colIndex The column index
38912      * @param {Number} rowIndex The row index
38913      * @return {Object}
38914      */
38915     getCellEditor : function(colIndex, rowIndex){
38916         return this.config[colIndex].editor;
38917     },
38918
38919     /**
38920      * Sets if a column is editable.
38921      * @param {Number} col The column index
38922      * @param {Boolean} editable True if the column is editable
38923      */
38924     setEditable : function(col, editable){
38925         this.config[col].editable = editable;
38926     },
38927
38928
38929     /**
38930      * Returns true if the column is hidden.
38931      * @param {Number} colIndex The column index
38932      * @return {Boolean}
38933      */
38934     isHidden : function(colIndex){
38935         return this.config[colIndex].hidden;
38936     },
38937
38938
38939     /**
38940      * Returns true if the column width cannot be changed
38941      */
38942     isFixed : function(colIndex){
38943         return this.config[colIndex].fixed;
38944     },
38945
38946     /**
38947      * Returns true if the column can be resized
38948      * @return {Boolean}
38949      */
38950     isResizable : function(colIndex){
38951         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38952     },
38953     /**
38954      * Sets if a column is hidden.
38955      * @param {Number} colIndex The column index
38956      * @param {Boolean} hidden True if the column is hidden
38957      */
38958     setHidden : function(colIndex, hidden){
38959         this.config[colIndex].hidden = hidden;
38960         this.totalWidth = null;
38961         this.fireEvent("hiddenchange", this, colIndex, hidden);
38962     },
38963
38964     /**
38965      * Sets the editor for a column.
38966      * @param {Number} col The column index
38967      * @param {Object} editor The editor object
38968      */
38969     setEditor : function(col, editor){
38970         this.config[col].editor = editor;
38971     }
38972 });
38973
38974 Roo.grid.ColumnModel.defaultRenderer = function(value){
38975         if(typeof value == "string" && value.length < 1){
38976             return "&#160;";
38977         }
38978         return value;
38979 };
38980
38981 // Alias for backwards compatibility
38982 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38983 /*
38984  * Based on:
38985  * Ext JS Library 1.1.1
38986  * Copyright(c) 2006-2007, Ext JS, LLC.
38987  *
38988  * Originally Released Under LGPL - original licence link has changed is not relivant.
38989  *
38990  * Fork - LGPL
38991  * <script type="text/javascript">
38992  */
38993
38994 /**
38995  * @class Roo.grid.AbstractSelectionModel
38996  * @extends Roo.util.Observable
38997  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38998  * implemented by descendant classes.  This class should not be directly instantiated.
38999  * @constructor
39000  */
39001 Roo.grid.AbstractSelectionModel = function(){
39002     this.locked = false;
39003     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39004 };
39005
39006 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39007     /** @ignore Called by the grid automatically. Do not call directly. */
39008     init : function(grid){
39009         this.grid = grid;
39010         this.initEvents();
39011     },
39012
39013     /**
39014      * Locks the selections.
39015      */
39016     lock : function(){
39017         this.locked = true;
39018     },
39019
39020     /**
39021      * Unlocks the selections.
39022      */
39023     unlock : function(){
39024         this.locked = false;
39025     },
39026
39027     /**
39028      * Returns true if the selections are locked.
39029      * @return {Boolean}
39030      */
39031     isLocked : function(){
39032         return this.locked;
39033     }
39034 });/*
39035  * Based on:
39036  * Ext JS Library 1.1.1
39037  * Copyright(c) 2006-2007, Ext JS, LLC.
39038  *
39039  * Originally Released Under LGPL - original licence link has changed is not relivant.
39040  *
39041  * Fork - LGPL
39042  * <script type="text/javascript">
39043  */
39044 /**
39045  * @extends Roo.grid.AbstractSelectionModel
39046  * @class Roo.grid.RowSelectionModel
39047  * The default SelectionModel used by {@link Roo.grid.Grid}.
39048  * It supports multiple selections and keyboard selection/navigation. 
39049  * @constructor
39050  * @param {Object} config
39051  */
39052 Roo.grid.RowSelectionModel = function(config){
39053     Roo.apply(this, config);
39054     this.selections = new Roo.util.MixedCollection(false, function(o){
39055         return o.id;
39056     });
39057
39058     this.last = false;
39059     this.lastActive = false;
39060
39061     this.addEvents({
39062         /**
39063              * @event selectionchange
39064              * Fires when the selection changes
39065              * @param {SelectionModel} this
39066              */
39067             "selectionchange" : true,
39068         /**
39069              * @event afterselectionchange
39070              * Fires after the selection changes (eg. by key press or clicking)
39071              * @param {SelectionModel} this
39072              */
39073             "afterselectionchange" : true,
39074         /**
39075              * @event beforerowselect
39076              * Fires when a row is selected being selected, return false to cancel.
39077              * @param {SelectionModel} this
39078              * @param {Number} rowIndex The selected index
39079              * @param {Boolean} keepExisting False if other selections will be cleared
39080              */
39081             "beforerowselect" : true,
39082         /**
39083              * @event rowselect
39084              * Fires when a row is selected.
39085              * @param {SelectionModel} this
39086              * @param {Number} rowIndex The selected index
39087              * @param {Roo.data.Record} r The record
39088              */
39089             "rowselect" : true,
39090         /**
39091              * @event rowdeselect
39092              * Fires when a row is deselected.
39093              * @param {SelectionModel} this
39094              * @param {Number} rowIndex The selected index
39095              */
39096         "rowdeselect" : true
39097     });
39098     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39099     this.locked = false;
39100 };
39101
39102 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39103     /**
39104      * @cfg {Boolean} singleSelect
39105      * True to allow selection of only one row at a time (defaults to false)
39106      */
39107     singleSelect : false,
39108
39109     // private
39110     initEvents : function(){
39111
39112         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39113             this.grid.on("mousedown", this.handleMouseDown, this);
39114         }else{ // allow click to work like normal
39115             this.grid.on("rowclick", this.handleDragableRowClick, this);
39116         }
39117
39118         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39119             "up" : function(e){
39120                 if(!e.shiftKey){
39121                     this.selectPrevious(e.shiftKey);
39122                 }else if(this.last !== false && this.lastActive !== false){
39123                     var last = this.last;
39124                     this.selectRange(this.last,  this.lastActive-1);
39125                     this.grid.getView().focusRow(this.lastActive);
39126                     if(last !== false){
39127                         this.last = last;
39128                     }
39129                 }else{
39130                     this.selectFirstRow();
39131                 }
39132                 this.fireEvent("afterselectionchange", this);
39133             },
39134             "down" : function(e){
39135                 if(!e.shiftKey){
39136                     this.selectNext(e.shiftKey);
39137                 }else if(this.last !== false && this.lastActive !== false){
39138                     var last = this.last;
39139                     this.selectRange(this.last,  this.lastActive+1);
39140                     this.grid.getView().focusRow(this.lastActive);
39141                     if(last !== false){
39142                         this.last = last;
39143                     }
39144                 }else{
39145                     this.selectFirstRow();
39146                 }
39147                 this.fireEvent("afterselectionchange", this);
39148             },
39149             scope: this
39150         });
39151
39152         var view = this.grid.view;
39153         view.on("refresh", this.onRefresh, this);
39154         view.on("rowupdated", this.onRowUpdated, this);
39155         view.on("rowremoved", this.onRemove, this);
39156     },
39157
39158     // private
39159     onRefresh : function(){
39160         var ds = this.grid.dataSource, i, v = this.grid.view;
39161         var s = this.selections;
39162         s.each(function(r){
39163             if((i = ds.indexOfId(r.id)) != -1){
39164                 v.onRowSelect(i);
39165             }else{
39166                 s.remove(r);
39167             }
39168         });
39169     },
39170
39171     // private
39172     onRemove : function(v, index, r){
39173         this.selections.remove(r);
39174     },
39175
39176     // private
39177     onRowUpdated : function(v, index, r){
39178         if(this.isSelected(r)){
39179             v.onRowSelect(index);
39180         }
39181     },
39182
39183     /**
39184      * Select records.
39185      * @param {Array} records The records to select
39186      * @param {Boolean} keepExisting (optional) True to keep existing selections
39187      */
39188     selectRecords : function(records, keepExisting){
39189         if(!keepExisting){
39190             this.clearSelections();
39191         }
39192         var ds = this.grid.dataSource;
39193         for(var i = 0, len = records.length; i < len; i++){
39194             this.selectRow(ds.indexOf(records[i]), true);
39195         }
39196     },
39197
39198     /**
39199      * Gets the number of selected rows.
39200      * @return {Number}
39201      */
39202     getCount : function(){
39203         return this.selections.length;
39204     },
39205
39206     /**
39207      * Selects the first row in the grid.
39208      */
39209     selectFirstRow : function(){
39210         this.selectRow(0);
39211     },
39212
39213     /**
39214      * Select the last row.
39215      * @param {Boolean} keepExisting (optional) True to keep existing selections
39216      */
39217     selectLastRow : function(keepExisting){
39218         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39219     },
39220
39221     /**
39222      * Selects the row immediately following the last selected row.
39223      * @param {Boolean} keepExisting (optional) True to keep existing selections
39224      */
39225     selectNext : function(keepExisting){
39226         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39227             this.selectRow(this.last+1, keepExisting);
39228             this.grid.getView().focusRow(this.last);
39229         }
39230     },
39231
39232     /**
39233      * Selects the row that precedes the last selected row.
39234      * @param {Boolean} keepExisting (optional) True to keep existing selections
39235      */
39236     selectPrevious : function(keepExisting){
39237         if(this.last){
39238             this.selectRow(this.last-1, keepExisting);
39239             this.grid.getView().focusRow(this.last);
39240         }
39241     },
39242
39243     /**
39244      * Returns the selected records
39245      * @return {Array} Array of selected records
39246      */
39247     getSelections : function(){
39248         return [].concat(this.selections.items);
39249     },
39250
39251     /**
39252      * Returns the first selected record.
39253      * @return {Record}
39254      */
39255     getSelected : function(){
39256         return this.selections.itemAt(0);
39257     },
39258
39259
39260     /**
39261      * Clears all selections.
39262      */
39263     clearSelections : function(fast){
39264         if(this.locked) return;
39265         if(fast !== true){
39266             var ds = this.grid.dataSource;
39267             var s = this.selections;
39268             s.each(function(r){
39269                 this.deselectRow(ds.indexOfId(r.id));
39270             }, this);
39271             s.clear();
39272         }else{
39273             this.selections.clear();
39274         }
39275         this.last = false;
39276     },
39277
39278
39279     /**
39280      * Selects all rows.
39281      */
39282     selectAll : function(){
39283         if(this.locked) return;
39284         this.selections.clear();
39285         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39286             this.selectRow(i, true);
39287         }
39288     },
39289
39290     /**
39291      * Returns True if there is a selection.
39292      * @return {Boolean}
39293      */
39294     hasSelection : function(){
39295         return this.selections.length > 0;
39296     },
39297
39298     /**
39299      * Returns True if the specified row is selected.
39300      * @param {Number/Record} record The record or index of the record to check
39301      * @return {Boolean}
39302      */
39303     isSelected : function(index){
39304         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39305         return (r && this.selections.key(r.id) ? true : false);
39306     },
39307
39308     /**
39309      * Returns True if the specified record id is selected.
39310      * @param {String} id The id of record to check
39311      * @return {Boolean}
39312      */
39313     isIdSelected : function(id){
39314         return (this.selections.key(id) ? true : false);
39315     },
39316
39317     // private
39318     handleMouseDown : function(e, t){
39319         var view = this.grid.getView(), rowIndex;
39320         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39321             return;
39322         };
39323         if(e.shiftKey && this.last !== false){
39324             var last = this.last;
39325             this.selectRange(last, rowIndex, e.ctrlKey);
39326             this.last = last; // reset the last
39327             view.focusRow(rowIndex);
39328         }else{
39329             var isSelected = this.isSelected(rowIndex);
39330             if(e.button !== 0 && isSelected){
39331                 view.focusRow(rowIndex);
39332             }else if(e.ctrlKey && isSelected){
39333                 this.deselectRow(rowIndex);
39334             }else if(!isSelected){
39335                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39336                 view.focusRow(rowIndex);
39337             }
39338         }
39339         this.fireEvent("afterselectionchange", this);
39340     },
39341     // private
39342     handleDragableRowClick :  function(grid, rowIndex, e) 
39343     {
39344         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39345             this.selectRow(rowIndex, false);
39346             grid.view.focusRow(rowIndex);
39347              this.fireEvent("afterselectionchange", this);
39348         }
39349     },
39350     
39351     /**
39352      * Selects multiple rows.
39353      * @param {Array} rows Array of the indexes of the row to select
39354      * @param {Boolean} keepExisting (optional) True to keep existing selections
39355      */
39356     selectRows : function(rows, keepExisting){
39357         if(!keepExisting){
39358             this.clearSelections();
39359         }
39360         for(var i = 0, len = rows.length; i < len; i++){
39361             this.selectRow(rows[i], true);
39362         }
39363     },
39364
39365     /**
39366      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39367      * @param {Number} startRow The index of the first row in the range
39368      * @param {Number} endRow The index of the last row in the range
39369      * @param {Boolean} keepExisting (optional) True to retain existing selections
39370      */
39371     selectRange : function(startRow, endRow, keepExisting){
39372         if(this.locked) return;
39373         if(!keepExisting){
39374             this.clearSelections();
39375         }
39376         if(startRow <= endRow){
39377             for(var i = startRow; i <= endRow; i++){
39378                 this.selectRow(i, true);
39379             }
39380         }else{
39381             for(var i = startRow; i >= endRow; i--){
39382                 this.selectRow(i, true);
39383             }
39384         }
39385     },
39386
39387     /**
39388      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39389      * @param {Number} startRow The index of the first row in the range
39390      * @param {Number} endRow The index of the last row in the range
39391      */
39392     deselectRange : function(startRow, endRow, preventViewNotify){
39393         if(this.locked) return;
39394         for(var i = startRow; i <= endRow; i++){
39395             this.deselectRow(i, preventViewNotify);
39396         }
39397     },
39398
39399     /**
39400      * Selects a row.
39401      * @param {Number} row The index of the row to select
39402      * @param {Boolean} keepExisting (optional) True to keep existing selections
39403      */
39404     selectRow : function(index, keepExisting, preventViewNotify){
39405         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39406         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39407             if(!keepExisting || this.singleSelect){
39408                 this.clearSelections();
39409             }
39410             var r = this.grid.dataSource.getAt(index);
39411             this.selections.add(r);
39412             this.last = this.lastActive = index;
39413             if(!preventViewNotify){
39414                 this.grid.getView().onRowSelect(index);
39415             }
39416             this.fireEvent("rowselect", this, index, r);
39417             this.fireEvent("selectionchange", this);
39418         }
39419     },
39420
39421     /**
39422      * Deselects a row.
39423      * @param {Number} row The index of the row to deselect
39424      */
39425     deselectRow : function(index, preventViewNotify){
39426         if(this.locked) return;
39427         if(this.last == index){
39428             this.last = false;
39429         }
39430         if(this.lastActive == index){
39431             this.lastActive = false;
39432         }
39433         var r = this.grid.dataSource.getAt(index);
39434         this.selections.remove(r);
39435         if(!preventViewNotify){
39436             this.grid.getView().onRowDeselect(index);
39437         }
39438         this.fireEvent("rowdeselect", this, index);
39439         this.fireEvent("selectionchange", this);
39440     },
39441
39442     // private
39443     restoreLast : function(){
39444         if(this._last){
39445             this.last = this._last;
39446         }
39447     },
39448
39449     // private
39450     acceptsNav : function(row, col, cm){
39451         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39452     },
39453
39454     // private
39455     onEditorKey : function(field, e){
39456         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39457         if(k == e.TAB){
39458             e.stopEvent();
39459             ed.completeEdit();
39460             if(e.shiftKey){
39461                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39462             }else{
39463                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39464             }
39465         }else if(k == e.ENTER && !e.ctrlKey){
39466             e.stopEvent();
39467             ed.completeEdit();
39468             if(e.shiftKey){
39469                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39470             }else{
39471                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39472             }
39473         }else if(k == e.ESC){
39474             ed.cancelEdit();
39475         }
39476         if(newCell){
39477             g.startEditing(newCell[0], newCell[1]);
39478         }
39479     }
39480 });/*
39481  * Based on:
39482  * Ext JS Library 1.1.1
39483  * Copyright(c) 2006-2007, Ext JS, LLC.
39484  *
39485  * Originally Released Under LGPL - original licence link has changed is not relivant.
39486  *
39487  * Fork - LGPL
39488  * <script type="text/javascript">
39489  */
39490 /**
39491  * @class Roo.grid.CellSelectionModel
39492  * @extends Roo.grid.AbstractSelectionModel
39493  * This class provides the basic implementation for cell selection in a grid.
39494  * @constructor
39495  * @param {Object} config The object containing the configuration of this model.
39496  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39497  */
39498 Roo.grid.CellSelectionModel = function(config){
39499     Roo.apply(this, config);
39500
39501     this.selection = null;
39502
39503     this.addEvents({
39504         /**
39505              * @event beforerowselect
39506              * Fires before a cell is selected.
39507              * @param {SelectionModel} this
39508              * @param {Number} rowIndex The selected row index
39509              * @param {Number} colIndex The selected cell index
39510              */
39511             "beforecellselect" : true,
39512         /**
39513              * @event cellselect
39514              * Fires when a cell is selected.
39515              * @param {SelectionModel} this
39516              * @param {Number} rowIndex The selected row index
39517              * @param {Number} colIndex The selected cell index
39518              */
39519             "cellselect" : true,
39520         /**
39521              * @event selectionchange
39522              * Fires when the active selection changes.
39523              * @param {SelectionModel} this
39524              * @param {Object} selection null for no selection or an object (o) with two properties
39525                 <ul>
39526                 <li>o.record: the record object for the row the selection is in</li>
39527                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39528                 </ul>
39529              */
39530             "selectionchange" : true,
39531         /**
39532              * @event tabend
39533              * Fires when the tab (or enter) was pressed on the last editable cell
39534              * You can use this to trigger add new row.
39535              * @param {SelectionModel} this
39536              */
39537             "tabend" : true,
39538          /**
39539              * @event beforeeditnext
39540              * Fires before the next editable sell is made active
39541              * You can use this to skip to another cell or fire the tabend
39542              *    if you set cell to false
39543              * @param {Object} eventdata object : { cell : [ row, col ] } 
39544              */
39545             "beforeeditnext" : true
39546     });
39547     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39548 };
39549
39550 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39551     
39552     enter_is_tab: false,
39553
39554     /** @ignore */
39555     initEvents : function(){
39556         this.grid.on("mousedown", this.handleMouseDown, this);
39557         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39558         var view = this.grid.view;
39559         view.on("refresh", this.onViewChange, this);
39560         view.on("rowupdated", this.onRowUpdated, this);
39561         view.on("beforerowremoved", this.clearSelections, this);
39562         view.on("beforerowsinserted", this.clearSelections, this);
39563         if(this.grid.isEditor){
39564             this.grid.on("beforeedit", this.beforeEdit,  this);
39565         }
39566     },
39567
39568         //private
39569     beforeEdit : function(e){
39570         this.select(e.row, e.column, false, true, e.record);
39571     },
39572
39573         //private
39574     onRowUpdated : function(v, index, r){
39575         if(this.selection && this.selection.record == r){
39576             v.onCellSelect(index, this.selection.cell[1]);
39577         }
39578     },
39579
39580         //private
39581     onViewChange : function(){
39582         this.clearSelections(true);
39583     },
39584
39585         /**
39586          * Returns the currently selected cell,.
39587          * @return {Array} The selected cell (row, column) or null if none selected.
39588          */
39589     getSelectedCell : function(){
39590         return this.selection ? this.selection.cell : null;
39591     },
39592
39593     /**
39594      * Clears all selections.
39595      * @param {Boolean} true to prevent the gridview from being notified about the change.
39596      */
39597     clearSelections : function(preventNotify){
39598         var s = this.selection;
39599         if(s){
39600             if(preventNotify !== true){
39601                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39602             }
39603             this.selection = null;
39604             this.fireEvent("selectionchange", this, null);
39605         }
39606     },
39607
39608     /**
39609      * Returns true if there is a selection.
39610      * @return {Boolean}
39611      */
39612     hasSelection : function(){
39613         return this.selection ? true : false;
39614     },
39615
39616     /** @ignore */
39617     handleMouseDown : function(e, t){
39618         var v = this.grid.getView();
39619         if(this.isLocked()){
39620             return;
39621         };
39622         var row = v.findRowIndex(t);
39623         var cell = v.findCellIndex(t);
39624         if(row !== false && cell !== false){
39625             this.select(row, cell);
39626         }
39627     },
39628
39629     /**
39630      * Selects a cell.
39631      * @param {Number} rowIndex
39632      * @param {Number} collIndex
39633      */
39634     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39635         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39636             this.clearSelections();
39637             r = r || this.grid.dataSource.getAt(rowIndex);
39638             this.selection = {
39639                 record : r,
39640                 cell : [rowIndex, colIndex]
39641             };
39642             if(!preventViewNotify){
39643                 var v = this.grid.getView();
39644                 v.onCellSelect(rowIndex, colIndex);
39645                 if(preventFocus !== true){
39646                     v.focusCell(rowIndex, colIndex);
39647                 }
39648             }
39649             this.fireEvent("cellselect", this, rowIndex, colIndex);
39650             this.fireEvent("selectionchange", this, this.selection);
39651         }
39652     },
39653
39654         //private
39655     isSelectable : function(rowIndex, colIndex, cm){
39656         return !cm.isHidden(colIndex);
39657     },
39658
39659     /** @ignore */
39660     handleKeyDown : function(e){
39661         //Roo.log('Cell Sel Model handleKeyDown');
39662         if(!e.isNavKeyPress()){
39663             return;
39664         }
39665         var g = this.grid, s = this.selection;
39666         if(!s){
39667             e.stopEvent();
39668             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39669             if(cell){
39670                 this.select(cell[0], cell[1]);
39671             }
39672             return;
39673         }
39674         var sm = this;
39675         var walk = function(row, col, step){
39676             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39677         };
39678         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39679         var newCell;
39680
39681       
39682
39683         switch(k){
39684             case e.TAB:
39685                 // handled by onEditorKey
39686                 if (g.isEditor && g.editing) {
39687                     return;
39688                 }
39689                 if(e.shiftKey) {
39690                     newCell = walk(r, c-1, -1);
39691                 } else {
39692                     newCell = walk(r, c+1, 1);
39693                 }
39694                 break;
39695             
39696             case e.DOWN:
39697                newCell = walk(r+1, c, 1);
39698                 break;
39699             
39700             case e.UP:
39701                 newCell = walk(r-1, c, -1);
39702                 break;
39703             
39704             case e.RIGHT:
39705                 newCell = walk(r, c+1, 1);
39706                 break;
39707             
39708             case e.LEFT:
39709                 newCell = walk(r, c-1, -1);
39710                 break;
39711             
39712             case e.ENTER:
39713                 
39714                 if(g.isEditor && !g.editing){
39715                    g.startEditing(r, c);
39716                    e.stopEvent();
39717                    return;
39718                 }
39719                 
39720                 
39721              break;
39722         };
39723         if(newCell){
39724             this.select(newCell[0], newCell[1]);
39725             e.stopEvent();
39726             
39727         }
39728     },
39729
39730     acceptsNav : function(row, col, cm){
39731         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39732     },
39733     /**
39734      * Selects a cell.
39735      * @param {Number} field (not used) - as it's normally used as a listener
39736      * @param {Number} e - event - fake it by using
39737      *
39738      * var e = Roo.EventObjectImpl.prototype;
39739      * e.keyCode = e.TAB
39740      *
39741      * 
39742      */
39743     onEditorKey : function(field, e){
39744         
39745         var k = e.getKey(),
39746             newCell,
39747             g = this.grid,
39748             ed = g.activeEditor,
39749             forward = false;
39750         ///Roo.log('onEditorKey' + k);
39751         
39752         
39753         if (this.enter_is_tab && k == e.ENTER) {
39754             k = e.TAB;
39755         }
39756         
39757         if(k == e.TAB){
39758             if(e.shiftKey){
39759                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39760             }else{
39761                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39762                 forward = true;
39763             }
39764             
39765             e.stopEvent();
39766             
39767         } else if(k == e.ENTER &&  !e.ctrlKey){
39768             ed.completeEdit();
39769             e.stopEvent();
39770             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39771         
39772                 } else if(k == e.ESC){
39773             ed.cancelEdit();
39774         }
39775                 
39776         if (newCell) {
39777             var ecall = { cell : newCell, forward : forward };
39778             this.fireEvent('beforeeditnext', ecall );
39779             newCell = ecall.cell;
39780                         forward = ecall.forward;
39781         }
39782                 
39783         if(newCell){
39784             //Roo.log('next cell after edit');
39785             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39786         } else if (forward) {
39787             // tabbed past last
39788             this.fireEvent.defer(100, this, ['tabend',this]);
39789         }
39790     }
39791 });/*
39792  * Based on:
39793  * Ext JS Library 1.1.1
39794  * Copyright(c) 2006-2007, Ext JS, LLC.
39795  *
39796  * Originally Released Under LGPL - original licence link has changed is not relivant.
39797  *
39798  * Fork - LGPL
39799  * <script type="text/javascript">
39800  */
39801  
39802 /**
39803  * @class Roo.grid.EditorGrid
39804  * @extends Roo.grid.Grid
39805  * Class for creating and editable grid.
39806  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39807  * The container MUST have some type of size defined for the grid to fill. The container will be 
39808  * automatically set to position relative if it isn't already.
39809  * @param {Object} dataSource The data model to bind to
39810  * @param {Object} colModel The column model with info about this grid's columns
39811  */
39812 Roo.grid.EditorGrid = function(container, config){
39813     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39814     this.getGridEl().addClass("xedit-grid");
39815
39816     if(!this.selModel){
39817         this.selModel = new Roo.grid.CellSelectionModel();
39818     }
39819
39820     this.activeEditor = null;
39821
39822         this.addEvents({
39823             /**
39824              * @event beforeedit
39825              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39826              * <ul style="padding:5px;padding-left:16px;">
39827              * <li>grid - This grid</li>
39828              * <li>record - The record being edited</li>
39829              * <li>field - The field name being edited</li>
39830              * <li>value - The value for the field being edited.</li>
39831              * <li>row - The grid row index</li>
39832              * <li>column - The grid column index</li>
39833              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39834              * </ul>
39835              * @param {Object} e An edit event (see above for description)
39836              */
39837             "beforeedit" : true,
39838             /**
39839              * @event afteredit
39840              * Fires after a cell is edited. <br />
39841              * <ul style="padding:5px;padding-left:16px;">
39842              * <li>grid - This grid</li>
39843              * <li>record - The record being edited</li>
39844              * <li>field - The field name being edited</li>
39845              * <li>value - The value being set</li>
39846              * <li>originalValue - The original value for the field, before the edit.</li>
39847              * <li>row - The grid row index</li>
39848              * <li>column - The grid column index</li>
39849              * </ul>
39850              * @param {Object} e An edit event (see above for description)
39851              */
39852             "afteredit" : true,
39853             /**
39854              * @event validateedit
39855              * Fires after a cell is edited, but before the value is set in the record. 
39856          * You can use this to modify the value being set in the field, Return false
39857              * to cancel the change. The edit event object has the following properties <br />
39858              * <ul style="padding:5px;padding-left:16px;">
39859          * <li>editor - This editor</li>
39860              * <li>grid - This grid</li>
39861              * <li>record - The record being edited</li>
39862              * <li>field - The field name being edited</li>
39863              * <li>value - The value being set</li>
39864              * <li>originalValue - The original value for the field, before the edit.</li>
39865              * <li>row - The grid row index</li>
39866              * <li>column - The grid column index</li>
39867              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39868              * </ul>
39869              * @param {Object} e An edit event (see above for description)
39870              */
39871             "validateedit" : true
39872         });
39873     this.on("bodyscroll", this.stopEditing,  this);
39874     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39875 };
39876
39877 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39878     /**
39879      * @cfg {Number} clicksToEdit
39880      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39881      */
39882     clicksToEdit: 2,
39883
39884     // private
39885     isEditor : true,
39886     // private
39887     trackMouseOver: false, // causes very odd FF errors
39888
39889     onCellDblClick : function(g, row, col){
39890         this.startEditing(row, col);
39891     },
39892
39893     onEditComplete : function(ed, value, startValue){
39894         this.editing = false;
39895         this.activeEditor = null;
39896         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39897         var r = ed.record;
39898         var field = this.colModel.getDataIndex(ed.col);
39899         var e = {
39900             grid: this,
39901             record: r,
39902             field: field,
39903             originalValue: startValue,
39904             value: value,
39905             row: ed.row,
39906             column: ed.col,
39907             cancel:false,
39908             editor: ed
39909         };
39910         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39911         cell.show();
39912           
39913         if(String(value) !== String(startValue)){
39914             
39915             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39916                 r.set(field, e.value);
39917                 // if we are dealing with a combo box..
39918                 // then we also set the 'name' colum to be the displayField
39919                 if (ed.field.displayField && ed.field.name) {
39920                     r.set(ed.field.name, ed.field.el.dom.value);
39921                 }
39922                 
39923                 delete e.cancel; //?? why!!!
39924                 this.fireEvent("afteredit", e);
39925             }
39926         } else {
39927             this.fireEvent("afteredit", e); // always fire it!
39928         }
39929         this.view.focusCell(ed.row, ed.col);
39930     },
39931
39932     /**
39933      * Starts editing the specified for the specified row/column
39934      * @param {Number} rowIndex
39935      * @param {Number} colIndex
39936      */
39937     startEditing : function(row, col){
39938         this.stopEditing();
39939         if(this.colModel.isCellEditable(col, row)){
39940             this.view.ensureVisible(row, col, true);
39941           
39942             var r = this.dataSource.getAt(row);
39943             var field = this.colModel.getDataIndex(col);
39944             var cell = Roo.get(this.view.getCell(row,col));
39945             var e = {
39946                 grid: this,
39947                 record: r,
39948                 field: field,
39949                 value: r.data[field],
39950                 row: row,
39951                 column: col,
39952                 cancel:false 
39953             };
39954             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39955                 this.editing = true;
39956                 var ed = this.colModel.getCellEditor(col, row);
39957                 
39958                 if (!ed) {
39959                     return;
39960                 }
39961                 if(!ed.rendered){
39962                     ed.render(ed.parentEl || document.body);
39963                 }
39964                 ed.field.reset();
39965                
39966                 cell.hide();
39967                 
39968                 (function(){ // complex but required for focus issues in safari, ie and opera
39969                     ed.row = row;
39970                     ed.col = col;
39971                     ed.record = r;
39972                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39973                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39974                     this.activeEditor = ed;
39975                     var v = r.data[field];
39976                     ed.startEdit(this.view.getCell(row, col), v);
39977                     // combo's with 'displayField and name set
39978                     if (ed.field.displayField && ed.field.name) {
39979                         ed.field.el.dom.value = r.data[ed.field.name];
39980                     }
39981                     
39982                     
39983                 }).defer(50, this);
39984             }
39985         }
39986     },
39987         
39988     /**
39989      * Stops any active editing
39990      */
39991     stopEditing : function(){
39992         if(this.activeEditor){
39993             this.activeEditor.completeEdit();
39994         }
39995         this.activeEditor = null;
39996     },
39997         
39998          /**
39999      * Called to get grid's drag proxy text, by default returns this.ddText.
40000      * @return {String}
40001      */
40002     getDragDropText : function(){
40003         var count = this.selModel.getSelectedCell() ? 1 : 0;
40004         return String.format(this.ddText, count, count == 1 ? '' : 's');
40005     }
40006         
40007 });/*
40008  * Based on:
40009  * Ext JS Library 1.1.1
40010  * Copyright(c) 2006-2007, Ext JS, LLC.
40011  *
40012  * Originally Released Under LGPL - original licence link has changed is not relivant.
40013  *
40014  * Fork - LGPL
40015  * <script type="text/javascript">
40016  */
40017
40018 // private - not really -- you end up using it !
40019 // This is a support class used internally by the Grid components
40020
40021 /**
40022  * @class Roo.grid.GridEditor
40023  * @extends Roo.Editor
40024  * Class for creating and editable grid elements.
40025  * @param {Object} config any settings (must include field)
40026  */
40027 Roo.grid.GridEditor = function(field, config){
40028     if (!config && field.field) {
40029         config = field;
40030         field = Roo.factory(config.field, Roo.form);
40031     }
40032     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40033     field.monitorTab = false;
40034 };
40035
40036 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40037     
40038     /**
40039      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40040      */
40041     
40042     alignment: "tl-tl",
40043     autoSize: "width",
40044     hideEl : false,
40045     cls: "x-small-editor x-grid-editor",
40046     shim:false,
40047     shadow:"frame"
40048 });/*
40049  * Based on:
40050  * Ext JS Library 1.1.1
40051  * Copyright(c) 2006-2007, Ext JS, LLC.
40052  *
40053  * Originally Released Under LGPL - original licence link has changed is not relivant.
40054  *
40055  * Fork - LGPL
40056  * <script type="text/javascript">
40057  */
40058   
40059
40060   
40061 Roo.grid.PropertyRecord = Roo.data.Record.create([
40062     {name:'name',type:'string'},  'value'
40063 ]);
40064
40065
40066 Roo.grid.PropertyStore = function(grid, source){
40067     this.grid = grid;
40068     this.store = new Roo.data.Store({
40069         recordType : Roo.grid.PropertyRecord
40070     });
40071     this.store.on('update', this.onUpdate,  this);
40072     if(source){
40073         this.setSource(source);
40074     }
40075     Roo.grid.PropertyStore.superclass.constructor.call(this);
40076 };
40077
40078
40079
40080 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40081     setSource : function(o){
40082         this.source = o;
40083         this.store.removeAll();
40084         var data = [];
40085         for(var k in o){
40086             if(this.isEditableValue(o[k])){
40087                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40088             }
40089         }
40090         this.store.loadRecords({records: data}, {}, true);
40091     },
40092
40093     onUpdate : function(ds, record, type){
40094         if(type == Roo.data.Record.EDIT){
40095             var v = record.data['value'];
40096             var oldValue = record.modified['value'];
40097             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40098                 this.source[record.id] = v;
40099                 record.commit();
40100                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40101             }else{
40102                 record.reject();
40103             }
40104         }
40105     },
40106
40107     getProperty : function(row){
40108        return this.store.getAt(row);
40109     },
40110
40111     isEditableValue: function(val){
40112         if(val && val instanceof Date){
40113             return true;
40114         }else if(typeof val == 'object' || typeof val == 'function'){
40115             return false;
40116         }
40117         return true;
40118     },
40119
40120     setValue : function(prop, value){
40121         this.source[prop] = value;
40122         this.store.getById(prop).set('value', value);
40123     },
40124
40125     getSource : function(){
40126         return this.source;
40127     }
40128 });
40129
40130 Roo.grid.PropertyColumnModel = function(grid, store){
40131     this.grid = grid;
40132     var g = Roo.grid;
40133     g.PropertyColumnModel.superclass.constructor.call(this, [
40134         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40135         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40136     ]);
40137     this.store = store;
40138     this.bselect = Roo.DomHelper.append(document.body, {
40139         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40140             {tag: 'option', value: 'true', html: 'true'},
40141             {tag: 'option', value: 'false', html: 'false'}
40142         ]
40143     });
40144     Roo.id(this.bselect);
40145     var f = Roo.form;
40146     this.editors = {
40147         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40148         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40149         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40150         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40151         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40152     };
40153     this.renderCellDelegate = this.renderCell.createDelegate(this);
40154     this.renderPropDelegate = this.renderProp.createDelegate(this);
40155 };
40156
40157 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40158     
40159     
40160     nameText : 'Name',
40161     valueText : 'Value',
40162     
40163     dateFormat : 'm/j/Y',
40164     
40165     
40166     renderDate : function(dateVal){
40167         return dateVal.dateFormat(this.dateFormat);
40168     },
40169
40170     renderBool : function(bVal){
40171         return bVal ? 'true' : 'false';
40172     },
40173
40174     isCellEditable : function(colIndex, rowIndex){
40175         return colIndex == 1;
40176     },
40177
40178     getRenderer : function(col){
40179         return col == 1 ?
40180             this.renderCellDelegate : this.renderPropDelegate;
40181     },
40182
40183     renderProp : function(v){
40184         return this.getPropertyName(v);
40185     },
40186
40187     renderCell : function(val){
40188         var rv = val;
40189         if(val instanceof Date){
40190             rv = this.renderDate(val);
40191         }else if(typeof val == 'boolean'){
40192             rv = this.renderBool(val);
40193         }
40194         return Roo.util.Format.htmlEncode(rv);
40195     },
40196
40197     getPropertyName : function(name){
40198         var pn = this.grid.propertyNames;
40199         return pn && pn[name] ? pn[name] : name;
40200     },
40201
40202     getCellEditor : function(colIndex, rowIndex){
40203         var p = this.store.getProperty(rowIndex);
40204         var n = p.data['name'], val = p.data['value'];
40205         
40206         if(typeof(this.grid.customEditors[n]) == 'string'){
40207             return this.editors[this.grid.customEditors[n]];
40208         }
40209         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40210             return this.grid.customEditors[n];
40211         }
40212         if(val instanceof Date){
40213             return this.editors['date'];
40214         }else if(typeof val == 'number'){
40215             return this.editors['number'];
40216         }else if(typeof val == 'boolean'){
40217             return this.editors['boolean'];
40218         }else{
40219             return this.editors['string'];
40220         }
40221     }
40222 });
40223
40224 /**
40225  * @class Roo.grid.PropertyGrid
40226  * @extends Roo.grid.EditorGrid
40227  * This class represents the  interface of a component based property grid control.
40228  * <br><br>Usage:<pre><code>
40229  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40230       
40231  });
40232  // set any options
40233  grid.render();
40234  * </code></pre>
40235   
40236  * @constructor
40237  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40238  * The container MUST have some type of size defined for the grid to fill. The container will be
40239  * automatically set to position relative if it isn't already.
40240  * @param {Object} config A config object that sets properties on this grid.
40241  */
40242 Roo.grid.PropertyGrid = function(container, config){
40243     config = config || {};
40244     var store = new Roo.grid.PropertyStore(this);
40245     this.store = store;
40246     var cm = new Roo.grid.PropertyColumnModel(this, store);
40247     store.store.sort('name', 'ASC');
40248     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40249         ds: store.store,
40250         cm: cm,
40251         enableColLock:false,
40252         enableColumnMove:false,
40253         stripeRows:false,
40254         trackMouseOver: false,
40255         clicksToEdit:1
40256     }, config));
40257     this.getGridEl().addClass('x-props-grid');
40258     this.lastEditRow = null;
40259     this.on('columnresize', this.onColumnResize, this);
40260     this.addEvents({
40261          /**
40262              * @event beforepropertychange
40263              * Fires before a property changes (return false to stop?)
40264              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40265              * @param {String} id Record Id
40266              * @param {String} newval New Value
40267          * @param {String} oldval Old Value
40268              */
40269         "beforepropertychange": true,
40270         /**
40271              * @event propertychange
40272              * Fires after a property changes
40273              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40274              * @param {String} id Record Id
40275              * @param {String} newval New Value
40276          * @param {String} oldval Old Value
40277              */
40278         "propertychange": true
40279     });
40280     this.customEditors = this.customEditors || {};
40281 };
40282 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40283     
40284      /**
40285      * @cfg {Object} customEditors map of colnames=> custom editors.
40286      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40287      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40288      * false disables editing of the field.
40289          */
40290     
40291       /**
40292      * @cfg {Object} propertyNames map of property Names to their displayed value
40293          */
40294     
40295     render : function(){
40296         Roo.grid.PropertyGrid.superclass.render.call(this);
40297         this.autoSize.defer(100, this);
40298     },
40299
40300     autoSize : function(){
40301         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40302         if(this.view){
40303             this.view.fitColumns();
40304         }
40305     },
40306
40307     onColumnResize : function(){
40308         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40309         this.autoSize();
40310     },
40311     /**
40312      * Sets the data for the Grid
40313      * accepts a Key => Value object of all the elements avaiable.
40314      * @param {Object} data  to appear in grid.
40315      */
40316     setSource : function(source){
40317         this.store.setSource(source);
40318         //this.autoSize();
40319     },
40320     /**
40321      * Gets all the data from the grid.
40322      * @return {Object} data  data stored in grid
40323      */
40324     getSource : function(){
40325         return this.store.getSource();
40326     }
40327 });/*
40328   
40329  * Licence LGPL
40330  
40331  */
40332  
40333 /**
40334  * @class Roo.grid.Calendar
40335  * @extends Roo.util.Grid
40336  * This class extends the Grid to provide a calendar widget
40337  * <br><br>Usage:<pre><code>
40338  var grid = new Roo.grid.Calendar("my-container-id", {
40339      ds: myDataStore,
40340      cm: myColModel,
40341      selModel: mySelectionModel,
40342      autoSizeColumns: true,
40343      monitorWindowResize: false,
40344      trackMouseOver: true
40345      eventstore : real data store..
40346  });
40347  // set any options
40348  grid.render();
40349   
40350   * @constructor
40351  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40352  * The container MUST have some type of size defined for the grid to fill. The container will be
40353  * automatically set to position relative if it isn't already.
40354  * @param {Object} config A config object that sets properties on this grid.
40355  */
40356 Roo.grid.Calendar = function(container, config){
40357         // initialize the container
40358         this.container = Roo.get(container);
40359         this.container.update("");
40360         this.container.setStyle("overflow", "hidden");
40361     this.container.addClass('x-grid-container');
40362
40363     this.id = this.container.id;
40364
40365     Roo.apply(this, config);
40366     // check and correct shorthanded configs
40367     
40368     var rows = [];
40369     var d =1;
40370     for (var r = 0;r < 6;r++) {
40371         
40372         rows[r]=[];
40373         for (var c =0;c < 7;c++) {
40374             rows[r][c]= '';
40375         }
40376     }
40377     if (this.eventStore) {
40378         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40379         this.eventStore.on('load',this.onLoad, this);
40380         this.eventStore.on('beforeload',this.clearEvents, this);
40381          
40382     }
40383     
40384     this.dataSource = new Roo.data.Store({
40385             proxy: new Roo.data.MemoryProxy(rows),
40386             reader: new Roo.data.ArrayReader({}, [
40387                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40388     });
40389
40390     this.dataSource.load();
40391     this.ds = this.dataSource;
40392     this.ds.xmodule = this.xmodule || false;
40393     
40394     
40395     var cellRender = function(v,x,r)
40396     {
40397         return String.format(
40398             '<div class="fc-day  fc-widget-content"><div>' +
40399                 '<div class="fc-event-container"></div>' +
40400                 '<div class="fc-day-number">{0}</div>'+
40401                 
40402                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40403             '</div></div>', v);
40404     
40405     }
40406     
40407     
40408     this.colModel = new Roo.grid.ColumnModel( [
40409         {
40410             xtype: 'ColumnModel',
40411             xns: Roo.grid,
40412             dataIndex : 'weekday0',
40413             header : 'Sunday',
40414             renderer : cellRender
40415         },
40416         {
40417             xtype: 'ColumnModel',
40418             xns: Roo.grid,
40419             dataIndex : 'weekday1',
40420             header : 'Monday',
40421             renderer : cellRender
40422         },
40423         {
40424             xtype: 'ColumnModel',
40425             xns: Roo.grid,
40426             dataIndex : 'weekday2',
40427             header : 'Tuesday',
40428             renderer : cellRender
40429         },
40430         {
40431             xtype: 'ColumnModel',
40432             xns: Roo.grid,
40433             dataIndex : 'weekday3',
40434             header : 'Wednesday',
40435             renderer : cellRender
40436         },
40437         {
40438             xtype: 'ColumnModel',
40439             xns: Roo.grid,
40440             dataIndex : 'weekday4',
40441             header : 'Thursday',
40442             renderer : cellRender
40443         },
40444         {
40445             xtype: 'ColumnModel',
40446             xns: Roo.grid,
40447             dataIndex : 'weekday5',
40448             header : 'Friday',
40449             renderer : cellRender
40450         },
40451         {
40452             xtype: 'ColumnModel',
40453             xns: Roo.grid,
40454             dataIndex : 'weekday6',
40455             header : 'Saturday',
40456             renderer : cellRender
40457         }
40458     ]);
40459     this.cm = this.colModel;
40460     this.cm.xmodule = this.xmodule || false;
40461  
40462         
40463           
40464     //this.selModel = new Roo.grid.CellSelectionModel();
40465     //this.sm = this.selModel;
40466     //this.selModel.init(this);
40467     
40468     
40469     if(this.width){
40470         this.container.setWidth(this.width);
40471     }
40472
40473     if(this.height){
40474         this.container.setHeight(this.height);
40475     }
40476     /** @private */
40477         this.addEvents({
40478         // raw events
40479         /**
40480          * @event click
40481          * The raw click event for the entire grid.
40482          * @param {Roo.EventObject} e
40483          */
40484         "click" : true,
40485         /**
40486          * @event dblclick
40487          * The raw dblclick event for the entire grid.
40488          * @param {Roo.EventObject} e
40489          */
40490         "dblclick" : true,
40491         /**
40492          * @event contextmenu
40493          * The raw contextmenu event for the entire grid.
40494          * @param {Roo.EventObject} e
40495          */
40496         "contextmenu" : true,
40497         /**
40498          * @event mousedown
40499          * The raw mousedown event for the entire grid.
40500          * @param {Roo.EventObject} e
40501          */
40502         "mousedown" : true,
40503         /**
40504          * @event mouseup
40505          * The raw mouseup event for the entire grid.
40506          * @param {Roo.EventObject} e
40507          */
40508         "mouseup" : true,
40509         /**
40510          * @event mouseover
40511          * The raw mouseover event for the entire grid.
40512          * @param {Roo.EventObject} e
40513          */
40514         "mouseover" : true,
40515         /**
40516          * @event mouseout
40517          * The raw mouseout event for the entire grid.
40518          * @param {Roo.EventObject} e
40519          */
40520         "mouseout" : true,
40521         /**
40522          * @event keypress
40523          * The raw keypress event for the entire grid.
40524          * @param {Roo.EventObject} e
40525          */
40526         "keypress" : true,
40527         /**
40528          * @event keydown
40529          * The raw keydown event for the entire grid.
40530          * @param {Roo.EventObject} e
40531          */
40532         "keydown" : true,
40533
40534         // custom events
40535
40536         /**
40537          * @event cellclick
40538          * Fires when a cell is clicked
40539          * @param {Grid} this
40540          * @param {Number} rowIndex
40541          * @param {Number} columnIndex
40542          * @param {Roo.EventObject} e
40543          */
40544         "cellclick" : true,
40545         /**
40546          * @event celldblclick
40547          * Fires when a cell is double clicked
40548          * @param {Grid} this
40549          * @param {Number} rowIndex
40550          * @param {Number} columnIndex
40551          * @param {Roo.EventObject} e
40552          */
40553         "celldblclick" : true,
40554         /**
40555          * @event rowclick
40556          * Fires when a row is clicked
40557          * @param {Grid} this
40558          * @param {Number} rowIndex
40559          * @param {Roo.EventObject} e
40560          */
40561         "rowclick" : true,
40562         /**
40563          * @event rowdblclick
40564          * Fires when a row is double clicked
40565          * @param {Grid} this
40566          * @param {Number} rowIndex
40567          * @param {Roo.EventObject} e
40568          */
40569         "rowdblclick" : true,
40570         /**
40571          * @event headerclick
40572          * Fires when a header is clicked
40573          * @param {Grid} this
40574          * @param {Number} columnIndex
40575          * @param {Roo.EventObject} e
40576          */
40577         "headerclick" : true,
40578         /**
40579          * @event headerdblclick
40580          * Fires when a header cell is double clicked
40581          * @param {Grid} this
40582          * @param {Number} columnIndex
40583          * @param {Roo.EventObject} e
40584          */
40585         "headerdblclick" : true,
40586         /**
40587          * @event rowcontextmenu
40588          * Fires when a row is right clicked
40589          * @param {Grid} this
40590          * @param {Number} rowIndex
40591          * @param {Roo.EventObject} e
40592          */
40593         "rowcontextmenu" : true,
40594         /**
40595          * @event cellcontextmenu
40596          * Fires when a cell is right clicked
40597          * @param {Grid} this
40598          * @param {Number} rowIndex
40599          * @param {Number} cellIndex
40600          * @param {Roo.EventObject} e
40601          */
40602          "cellcontextmenu" : true,
40603         /**
40604          * @event headercontextmenu
40605          * Fires when a header is right clicked
40606          * @param {Grid} this
40607          * @param {Number} columnIndex
40608          * @param {Roo.EventObject} e
40609          */
40610         "headercontextmenu" : true,
40611         /**
40612          * @event bodyscroll
40613          * Fires when the body element is scrolled
40614          * @param {Number} scrollLeft
40615          * @param {Number} scrollTop
40616          */
40617         "bodyscroll" : true,
40618         /**
40619          * @event columnresize
40620          * Fires when the user resizes a column
40621          * @param {Number} columnIndex
40622          * @param {Number} newSize
40623          */
40624         "columnresize" : true,
40625         /**
40626          * @event columnmove
40627          * Fires when the user moves a column
40628          * @param {Number} oldIndex
40629          * @param {Number} newIndex
40630          */
40631         "columnmove" : true,
40632         /**
40633          * @event startdrag
40634          * Fires when row(s) start being dragged
40635          * @param {Grid} this
40636          * @param {Roo.GridDD} dd The drag drop object
40637          * @param {event} e The raw browser event
40638          */
40639         "startdrag" : true,
40640         /**
40641          * @event enddrag
40642          * Fires when a drag operation is complete
40643          * @param {Grid} this
40644          * @param {Roo.GridDD} dd The drag drop object
40645          * @param {event} e The raw browser event
40646          */
40647         "enddrag" : true,
40648         /**
40649          * @event dragdrop
40650          * Fires when dragged row(s) are dropped on a valid DD target
40651          * @param {Grid} this
40652          * @param {Roo.GridDD} dd The drag drop object
40653          * @param {String} targetId The target drag drop object
40654          * @param {event} e The raw browser event
40655          */
40656         "dragdrop" : true,
40657         /**
40658          * @event dragover
40659          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40660          * @param {Grid} this
40661          * @param {Roo.GridDD} dd The drag drop object
40662          * @param {String} targetId The target drag drop object
40663          * @param {event} e The raw browser event
40664          */
40665         "dragover" : true,
40666         /**
40667          * @event dragenter
40668          *  Fires when the dragged row(s) first cross another DD target while being dragged
40669          * @param {Grid} this
40670          * @param {Roo.GridDD} dd The drag drop object
40671          * @param {String} targetId The target drag drop object
40672          * @param {event} e The raw browser event
40673          */
40674         "dragenter" : true,
40675         /**
40676          * @event dragout
40677          * Fires when the dragged row(s) leave another DD target while being dragged
40678          * @param {Grid} this
40679          * @param {Roo.GridDD} dd The drag drop object
40680          * @param {String} targetId The target drag drop object
40681          * @param {event} e The raw browser event
40682          */
40683         "dragout" : true,
40684         /**
40685          * @event rowclass
40686          * Fires when a row is rendered, so you can change add a style to it.
40687          * @param {GridView} gridview   The grid view
40688          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
40689          */
40690         'rowclass' : true,
40691
40692         /**
40693          * @event render
40694          * Fires when the grid is rendered
40695          * @param {Grid} grid
40696          */
40697         'render' : true,
40698             /**
40699              * @event select
40700              * Fires when a date is selected
40701              * @param {DatePicker} this
40702              * @param {Date} date The selected date
40703              */
40704         'select': true,
40705         /**
40706              * @event monthchange
40707              * Fires when the displayed month changes 
40708              * @param {DatePicker} this
40709              * @param {Date} date The selected month
40710              */
40711         'monthchange': true,
40712         /**
40713              * @event evententer
40714              * Fires when mouse over an event
40715              * @param {Calendar} this
40716              * @param {event} Event
40717              */
40718         'evententer': true,
40719         /**
40720              * @event eventleave
40721              * Fires when the mouse leaves an
40722              * @param {Calendar} this
40723              * @param {event}
40724              */
40725         'eventleave': true,
40726         /**
40727              * @event eventclick
40728              * Fires when the mouse click an
40729              * @param {Calendar} this
40730              * @param {event}
40731              */
40732         'eventclick': true,
40733         /**
40734              * @event eventrender
40735              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
40736              * @param {Calendar} this
40737              * @param {data} data to be modified
40738              */
40739         'eventrender': true
40740         
40741     });
40742
40743     Roo.grid.Grid.superclass.constructor.call(this);
40744     this.on('render', function() {
40745         this.view.el.addClass('x-grid-cal'); 
40746         
40747         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
40748
40749     },this);
40750     
40751     if (!Roo.grid.Calendar.style) {
40752         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
40753             
40754             
40755             '.x-grid-cal .x-grid-col' :  {
40756                 height: 'auto !important',
40757                 'vertical-align': 'top'
40758             },
40759             '.x-grid-cal  .fc-event-hori' : {
40760                 height: '14px'
40761             }
40762              
40763             
40764         }, Roo.id());
40765     }
40766
40767     
40768     
40769 };
40770 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
40771     /**
40772      * @cfg {Store} eventStore The store that loads events.
40773      */
40774     eventStore : 25,
40775
40776      
40777     activeDate : false,
40778     startDay : 0,
40779     autoWidth : true,
40780     monitorWindowResize : false,
40781
40782     
40783     resizeColumns : function() {
40784         var col = (this.view.el.getWidth() / 7) - 3;
40785         // loop through cols, and setWidth
40786         for(var i =0 ; i < 7 ; i++){
40787             this.cm.setColumnWidth(i, col);
40788         }
40789     },
40790      setDate :function(date) {
40791         
40792         Roo.log('setDate?');
40793         
40794         this.resizeColumns();
40795         var vd = this.activeDate;
40796         this.activeDate = date;
40797 //        if(vd && this.el){
40798 //            var t = date.getTime();
40799 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
40800 //                Roo.log('using add remove');
40801 //                
40802 //                this.fireEvent('monthchange', this, date);
40803 //                
40804 //                this.cells.removeClass("fc-state-highlight");
40805 //                this.cells.each(function(c){
40806 //                   if(c.dateValue == t){
40807 //                       c.addClass("fc-state-highlight");
40808 //                       setTimeout(function(){
40809 //                            try{c.dom.firstChild.focus();}catch(e){}
40810 //                       }, 50);
40811 //                       return false;
40812 //                   }
40813 //                   return true;
40814 //                });
40815 //                return;
40816 //            }
40817 //        }
40818         
40819         var days = date.getDaysInMonth();
40820         
40821         var firstOfMonth = date.getFirstDateOfMonth();
40822         var startingPos = firstOfMonth.getDay()-this.startDay;
40823         
40824         if(startingPos < this.startDay){
40825             startingPos += 7;
40826         }
40827         
40828         var pm = date.add(Date.MONTH, -1);
40829         var prevStart = pm.getDaysInMonth()-startingPos;
40830 //        
40831         
40832         
40833         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40834         
40835         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
40836         //this.cells.addClassOnOver('fc-state-hover');
40837         
40838         var cells = this.cells.elements;
40839         var textEls = this.textNodes;
40840         
40841         //Roo.each(cells, function(cell){
40842         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
40843         //});
40844         
40845         days += startingPos;
40846
40847         // convert everything to numbers so it's fast
40848         var day = 86400000;
40849         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
40850         //Roo.log(d);
40851         //Roo.log(pm);
40852         //Roo.log(prevStart);
40853         
40854         var today = new Date().clearTime().getTime();
40855         var sel = date.clearTime().getTime();
40856         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
40857         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
40858         var ddMatch = this.disabledDatesRE;
40859         var ddText = this.disabledDatesText;
40860         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
40861         var ddaysText = this.disabledDaysText;
40862         var format = this.format;
40863         
40864         var setCellClass = function(cal, cell){
40865             
40866             //Roo.log('set Cell Class');
40867             cell.title = "";
40868             var t = d.getTime();
40869             
40870             //Roo.log(d);
40871             
40872             
40873             cell.dateValue = t;
40874             if(t == today){
40875                 cell.className += " fc-today";
40876                 cell.className += " fc-state-highlight";
40877                 cell.title = cal.todayText;
40878             }
40879             if(t == sel){
40880                 // disable highlight in other month..
40881                 cell.className += " fc-state-highlight";
40882                 
40883             }
40884             // disabling
40885             if(t < min) {
40886                 //cell.className = " fc-state-disabled";
40887                 cell.title = cal.minText;
40888                 return;
40889             }
40890             if(t > max) {
40891                 //cell.className = " fc-state-disabled";
40892                 cell.title = cal.maxText;
40893                 return;
40894             }
40895             if(ddays){
40896                 if(ddays.indexOf(d.getDay()) != -1){
40897                     // cell.title = ddaysText;
40898                    // cell.className = " fc-state-disabled";
40899                 }
40900             }
40901             if(ddMatch && format){
40902                 var fvalue = d.dateFormat(format);
40903                 if(ddMatch.test(fvalue)){
40904                     cell.title = ddText.replace("%0", fvalue);
40905                    cell.className = " fc-state-disabled";
40906                 }
40907             }
40908             
40909             if (!cell.initialClassName) {
40910                 cell.initialClassName = cell.dom.className;
40911             }
40912             
40913             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
40914         };
40915
40916         var i = 0;
40917         
40918         for(; i < startingPos; i++) {
40919             cells[i].dayName =  (++prevStart);
40920             Roo.log(textEls[i]);
40921             d.setDate(d.getDate()+1);
40922             
40923             //cells[i].className = "fc-past fc-other-month";
40924             setCellClass(this, cells[i]);
40925         }
40926         
40927         var intDay = 0;
40928         
40929         for(; i < days; i++){
40930             intDay = i - startingPos + 1;
40931             cells[i].dayName =  (intDay);
40932             d.setDate(d.getDate()+1);
40933             
40934             cells[i].className = ''; // "x-date-active";
40935             setCellClass(this, cells[i]);
40936         }
40937         var extraDays = 0;
40938         
40939         for(; i < 42; i++) {
40940             //textEls[i].innerHTML = (++extraDays);
40941             
40942             d.setDate(d.getDate()+1);
40943             cells[i].dayName = (++extraDays);
40944             cells[i].className = "fc-future fc-other-month";
40945             setCellClass(this, cells[i]);
40946         }
40947         
40948         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
40949         
40950         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
40951         
40952         // this will cause all the cells to mis
40953         var rows= [];
40954         var i =0;
40955         for (var r = 0;r < 6;r++) {
40956             for (var c =0;c < 7;c++) {
40957                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
40958             }    
40959         }
40960         
40961         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40962         for(i=0;i<cells.length;i++) {
40963             
40964             this.cells.elements[i].dayName = cells[i].dayName ;
40965             this.cells.elements[i].className = cells[i].className;
40966             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
40967             this.cells.elements[i].title = cells[i].title ;
40968             this.cells.elements[i].dateValue = cells[i].dateValue ;
40969         }
40970         
40971         
40972         
40973         
40974         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
40975         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
40976         
40977         ////if(totalRows != 6){
40978             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
40979            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
40980        // }
40981         
40982         this.fireEvent('monthchange', this, date);
40983         
40984         
40985     },
40986  /**
40987      * Returns the grid's SelectionModel.
40988      * @return {SelectionModel}
40989      */
40990     getSelectionModel : function(){
40991         if(!this.selModel){
40992             this.selModel = new Roo.grid.CellSelectionModel();
40993         }
40994         return this.selModel;
40995     },
40996
40997     load: function() {
40998         this.eventStore.load()
40999         
41000         
41001         
41002     },
41003     
41004     findCell : function(dt) {
41005         dt = dt.clearTime().getTime();
41006         var ret = false;
41007         this.cells.each(function(c){
41008             //Roo.log("check " +c.dateValue + '?=' + dt);
41009             if(c.dateValue == dt){
41010                 ret = c;
41011                 return false;
41012             }
41013             return true;
41014         });
41015         
41016         return ret;
41017     },
41018     
41019     findCells : function(rec) {
41020         var s = rec.data.start_dt.clone().clearTime().getTime();
41021        // Roo.log(s);
41022         var e= rec.data.end_dt.clone().clearTime().getTime();
41023        // Roo.log(e);
41024         var ret = [];
41025         this.cells.each(function(c){
41026              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41027             
41028             if(c.dateValue > e){
41029                 return ;
41030             }
41031             if(c.dateValue < s){
41032                 return ;
41033             }
41034             ret.push(c);
41035         });
41036         
41037         return ret;    
41038     },
41039     
41040     findBestRow: function(cells)
41041     {
41042         var ret = 0;
41043         
41044         for (var i =0 ; i < cells.length;i++) {
41045             ret  = Math.max(cells[i].rows || 0,ret);
41046         }
41047         return ret;
41048         
41049     },
41050     
41051     
41052     addItem : function(rec)
41053     {
41054         // look for vertical location slot in
41055         var cells = this.findCells(rec);
41056         
41057         rec.row = this.findBestRow(cells);
41058         
41059         // work out the location.
41060         
41061         var crow = false;
41062         var rows = [];
41063         for(var i =0; i < cells.length; i++) {
41064             if (!crow) {
41065                 crow = {
41066                     start : cells[i],
41067                     end :  cells[i]
41068                 };
41069                 continue;
41070             }
41071             if (crow.start.getY() == cells[i].getY()) {
41072                 // on same row.
41073                 crow.end = cells[i];
41074                 continue;
41075             }
41076             // different row.
41077             rows.push(crow);
41078             crow = {
41079                 start: cells[i],
41080                 end : cells[i]
41081             };
41082             
41083         }
41084         
41085         rows.push(crow);
41086         rec.els = [];
41087         rec.rows = rows;
41088         rec.cells = cells;
41089         for (var i = 0; i < cells.length;i++) {
41090             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41091             
41092         }
41093         
41094         
41095     },
41096     
41097     clearEvents: function() {
41098         
41099         if (!this.eventStore.getCount()) {
41100             return;
41101         }
41102         // reset number of rows in cells.
41103         Roo.each(this.cells.elements, function(c){
41104             c.rows = 0;
41105         });
41106         
41107         this.eventStore.each(function(e) {
41108             this.clearEvent(e);
41109         },this);
41110         
41111     },
41112     
41113     clearEvent : function(ev)
41114     {
41115         if (ev.els) {
41116             Roo.each(ev.els, function(el) {
41117                 el.un('mouseenter' ,this.onEventEnter, this);
41118                 el.un('mouseleave' ,this.onEventLeave, this);
41119                 el.remove();
41120             },this);
41121             ev.els = [];
41122         }
41123     },
41124     
41125     
41126     renderEvent : function(ev,ctr) {
41127         if (!ctr) {
41128              ctr = this.view.el.select('.fc-event-container',true).first();
41129         }
41130         
41131          
41132         this.clearEvent(ev);
41133             //code
41134        
41135         
41136         
41137         ev.els = [];
41138         var cells = ev.cells;
41139         var rows = ev.rows;
41140         this.fireEvent('eventrender', this, ev);
41141         
41142         for(var i =0; i < rows.length; i++) {
41143             
41144             cls = '';
41145             if (i == 0) {
41146                 cls += ' fc-event-start';
41147             }
41148             if ((i+1) == rows.length) {
41149                 cls += ' fc-event-end';
41150             }
41151             
41152             //Roo.log(ev.data);
41153             // how many rows should it span..
41154             var cg = this.eventTmpl.append(ctr,Roo.apply({
41155                 fccls : cls
41156                 
41157             }, ev.data) , true);
41158             
41159             
41160             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41161             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41162             cg.on('click', this.onEventClick, this, ev);
41163             
41164             ev.els.push(cg);
41165             
41166             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41167             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41168             //Roo.log(cg);
41169              
41170             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41171             cg.setWidth(ebox.right - sbox.x -2);
41172         }
41173     },
41174     
41175     renderEvents: function()
41176     {   
41177         // first make sure there is enough space..
41178         
41179         if (!this.eventTmpl) {
41180             this.eventTmpl = new Roo.Template(
41181                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41182                     '<div class="fc-event-inner">' +
41183                         '<span class="fc-event-time">{time}</span>' +
41184                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41185                     '</div>' +
41186                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41187                 '</div>'
41188             );
41189                 
41190         }
41191                
41192         
41193         
41194         this.cells.each(function(c) {
41195             //Roo.log(c.select('.fc-day-content div',true).first());
41196             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41197         });
41198         
41199         var ctr = this.view.el.select('.fc-event-container',true).first();
41200         
41201         var cls;
41202         this.eventStore.each(function(ev){
41203             
41204             this.renderEvent(ev);
41205              
41206              
41207         }, this);
41208         this.view.layout();
41209         
41210     },
41211     
41212     onEventEnter: function (e, el,event,d) {
41213         this.fireEvent('evententer', this, el, event);
41214     },
41215     
41216     onEventLeave: function (e, el,event,d) {
41217         this.fireEvent('eventleave', this, el, event);
41218     },
41219     
41220     onEventClick: function (e, el,event,d) {
41221         this.fireEvent('eventclick', this, el, event);
41222     },
41223     
41224     onMonthChange: function () {
41225         this.store.load();
41226     },
41227     
41228     onLoad: function () {
41229         
41230         //Roo.log('calendar onload');
41231 //         
41232         if(this.eventStore.getCount() > 0){
41233             
41234            
41235             
41236             this.eventStore.each(function(d){
41237                 
41238                 
41239                 // FIXME..
41240                 var add =   d.data;
41241                 if (typeof(add.end_dt) == 'undefined')  {
41242                     Roo.log("Missing End time in calendar data: ");
41243                     Roo.log(d);
41244                     return;
41245                 }
41246                 if (typeof(add.start_dt) == 'undefined')  {
41247                     Roo.log("Missing Start time in calendar data: ");
41248                     Roo.log(d);
41249                     return;
41250                 }
41251                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41252                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41253                 add.id = add.id || d.id;
41254                 add.title = add.title || '??';
41255                 
41256                 this.addItem(d);
41257                 
41258              
41259             },this);
41260         }
41261         
41262         this.renderEvents();
41263     }
41264     
41265
41266 });
41267 /*
41268  grid : {
41269                 xtype: 'Grid',
41270                 xns: Roo.grid,
41271                 listeners : {
41272                     render : function ()
41273                     {
41274                         _this.grid = this;
41275                         
41276                         if (!this.view.el.hasClass('course-timesheet')) {
41277                             this.view.el.addClass('course-timesheet');
41278                         }
41279                         if (this.tsStyle) {
41280                             this.ds.load({});
41281                             return; 
41282                         }
41283                         Roo.log('width');
41284                         Roo.log(_this.grid.view.el.getWidth());
41285                         
41286                         
41287                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41288                             '.course-timesheet .x-grid-row' : {
41289                                 height: '80px'
41290                             },
41291                             '.x-grid-row td' : {
41292                                 'vertical-align' : 0
41293                             },
41294                             '.course-edit-link' : {
41295                                 'color' : 'blue',
41296                                 'text-overflow' : 'ellipsis',
41297                                 'overflow' : 'hidden',
41298                                 'white-space' : 'nowrap',
41299                                 'cursor' : 'pointer'
41300                             },
41301                             '.sub-link' : {
41302                                 'color' : 'green'
41303                             },
41304                             '.de-act-sup-link' : {
41305                                 'color' : 'purple',
41306                                 'text-decoration' : 'line-through'
41307                             },
41308                             '.de-act-link' : {
41309                                 'color' : 'red',
41310                                 'text-decoration' : 'line-through'
41311                             },
41312                             '.course-timesheet .course-highlight' : {
41313                                 'border-top-style': 'dashed !important',
41314                                 'border-bottom-bottom': 'dashed !important'
41315                             },
41316                             '.course-timesheet .course-item' : {
41317                                 'font-family'   : 'tahoma, arial, helvetica',
41318                                 'font-size'     : '11px',
41319                                 'overflow'      : 'hidden',
41320                                 'padding-left'  : '10px',
41321                                 'padding-right' : '10px',
41322                                 'padding-top' : '10px' 
41323                             }
41324                             
41325                         }, Roo.id());
41326                                 this.ds.load({});
41327                     }
41328                 },
41329                 autoWidth : true,
41330                 monitorWindowResize : false,
41331                 cellrenderer : function(v,x,r)
41332                 {
41333                     return v;
41334                 },
41335                 sm : {
41336                     xtype: 'CellSelectionModel',
41337                     xns: Roo.grid
41338                 },
41339                 dataSource : {
41340                     xtype: 'Store',
41341                     xns: Roo.data,
41342                     listeners : {
41343                         beforeload : function (_self, options)
41344                         {
41345                             options.params = options.params || {};
41346                             options.params._month = _this.monthField.getValue();
41347                             options.params.limit = 9999;
41348                             options.params['sort'] = 'when_dt';    
41349                             options.params['dir'] = 'ASC';    
41350                             this.proxy.loadResponse = this.loadResponse;
41351                             Roo.log("load?");
41352                             //this.addColumns();
41353                         },
41354                         load : function (_self, records, options)
41355                         {
41356                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41357                                 // if you click on the translation.. you can edit it...
41358                                 var el = Roo.get(this);
41359                                 var id = el.dom.getAttribute('data-id');
41360                                 var d = el.dom.getAttribute('data-date');
41361                                 var t = el.dom.getAttribute('data-time');
41362                                 //var id = this.child('span').dom.textContent;
41363                                 
41364                                 //Roo.log(this);
41365                                 Pman.Dialog.CourseCalendar.show({
41366                                     id : id,
41367                                     when_d : d,
41368                                     when_t : t,
41369                                     productitem_active : id ? 1 : 0
41370                                 }, function() {
41371                                     _this.grid.ds.load({});
41372                                 });
41373                            
41374                            });
41375                            
41376                            _this.panel.fireEvent('resize', [ '', '' ]);
41377                         }
41378                     },
41379                     loadResponse : function(o, success, response){
41380                             // this is overridden on before load..
41381                             
41382                             Roo.log("our code?");       
41383                             //Roo.log(success);
41384                             //Roo.log(response)
41385                             delete this.activeRequest;
41386                             if(!success){
41387                                 this.fireEvent("loadexception", this, o, response);
41388                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41389                                 return;
41390                             }
41391                             var result;
41392                             try {
41393                                 result = o.reader.read(response);
41394                             }catch(e){
41395                                 Roo.log("load exception?");
41396                                 this.fireEvent("loadexception", this, o, response, e);
41397                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41398                                 return;
41399                             }
41400                             Roo.log("ready...");        
41401                             // loop through result.records;
41402                             // and set this.tdate[date] = [] << array of records..
41403                             _this.tdata  = {};
41404                             Roo.each(result.records, function(r){
41405                                 //Roo.log(r.data);
41406                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41407                                     _this.tdata[r.data.when_dt.format('j')] = [];
41408                                 }
41409                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41410                             });
41411                             
41412                             //Roo.log(_this.tdata);
41413                             
41414                             result.records = [];
41415                             result.totalRecords = 6;
41416                     
41417                             // let's generate some duumy records for the rows.
41418                             //var st = _this.dateField.getValue();
41419                             
41420                             // work out monday..
41421                             //st = st.add(Date.DAY, -1 * st.format('w'));
41422                             
41423                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41424                             
41425                             var firstOfMonth = date.getFirstDayOfMonth();
41426                             var days = date.getDaysInMonth();
41427                             var d = 1;
41428                             var firstAdded = false;
41429                             for (var i = 0; i < result.totalRecords ; i++) {
41430                                 //var d= st.add(Date.DAY, i);
41431                                 var row = {};
41432                                 var added = 0;
41433                                 for(var w = 0 ; w < 7 ; w++){
41434                                     if(!firstAdded && firstOfMonth != w){
41435                                         continue;
41436                                     }
41437                                     if(d > days){
41438                                         continue;
41439                                     }
41440                                     firstAdded = true;
41441                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41442                                     row['weekday'+w] = String.format(
41443                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41444                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41445                                                     d,
41446                                                     date.format('Y-m-')+dd
41447                                                 );
41448                                     added++;
41449                                     if(typeof(_this.tdata[d]) != 'undefined'){
41450                                         Roo.each(_this.tdata[d], function(r){
41451                                             var is_sub = '';
41452                                             var deactive = '';
41453                                             var id = r.id;
41454                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41455                                             if(r.parent_id*1>0){
41456                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41457                                                 id = r.parent_id;
41458                                             }
41459                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41460                                                 deactive = 'de-act-link';
41461                                             }
41462                                             
41463                                             row['weekday'+w] += String.format(
41464                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41465                                                     id, //0
41466                                                     r.product_id_name, //1
41467                                                     r.when_dt.format('h:ia'), //2
41468                                                     is_sub, //3
41469                                                     deactive, //4
41470                                                     desc // 5
41471                                             );
41472                                         });
41473                                     }
41474                                     d++;
41475                                 }
41476                                 
41477                                 // only do this if something added..
41478                                 if(added > 0){ 
41479                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41480                                 }
41481                                 
41482                                 
41483                                 // push it twice. (second one with an hour..
41484                                 
41485                             }
41486                             //Roo.log(result);
41487                             this.fireEvent("load", this, o, o.request.arg);
41488                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41489                         },
41490                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41491                     proxy : {
41492                         xtype: 'HttpProxy',
41493                         xns: Roo.data,
41494                         method : 'GET',
41495                         url : baseURL + '/Roo/Shop_course.php'
41496                     },
41497                     reader : {
41498                         xtype: 'JsonReader',
41499                         xns: Roo.data,
41500                         id : 'id',
41501                         fields : [
41502                             {
41503                                 'name': 'id',
41504                                 'type': 'int'
41505                             },
41506                             {
41507                                 'name': 'when_dt',
41508                                 'type': 'string'
41509                             },
41510                             {
41511                                 'name': 'end_dt',
41512                                 'type': 'string'
41513                             },
41514                             {
41515                                 'name': 'parent_id',
41516                                 'type': 'int'
41517                             },
41518                             {
41519                                 'name': 'product_id',
41520                                 'type': 'int'
41521                             },
41522                             {
41523                                 'name': 'productitem_id',
41524                                 'type': 'int'
41525                             },
41526                             {
41527                                 'name': 'guid',
41528                                 'type': 'int'
41529                             }
41530                         ]
41531                     }
41532                 },
41533                 toolbar : {
41534                     xtype: 'Toolbar',
41535                     xns: Roo,
41536                     items : [
41537                         {
41538                             xtype: 'Button',
41539                             xns: Roo.Toolbar,
41540                             listeners : {
41541                                 click : function (_self, e)
41542                                 {
41543                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41544                                     sd.setMonth(sd.getMonth()-1);
41545                                     _this.monthField.setValue(sd.format('Y-m-d'));
41546                                     _this.grid.ds.load({});
41547                                 }
41548                             },
41549                             text : "Back"
41550                         },
41551                         {
41552                             xtype: 'Separator',
41553                             xns: Roo.Toolbar
41554                         },
41555                         {
41556                             xtype: 'MonthField',
41557                             xns: Roo.form,
41558                             listeners : {
41559                                 render : function (_self)
41560                                 {
41561                                     _this.monthField = _self;
41562                                    // _this.monthField.set  today
41563                                 },
41564                                 select : function (combo, date)
41565                                 {
41566                                     _this.grid.ds.load({});
41567                                 }
41568                             },
41569                             value : (function() { return new Date(); })()
41570                         },
41571                         {
41572                             xtype: 'Separator',
41573                             xns: Roo.Toolbar
41574                         },
41575                         {
41576                             xtype: 'TextItem',
41577                             xns: Roo.Toolbar,
41578                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41579                         },
41580                         {
41581                             xtype: 'Fill',
41582                             xns: Roo.Toolbar
41583                         },
41584                         {
41585                             xtype: 'Button',
41586                             xns: Roo.Toolbar,
41587                             listeners : {
41588                                 click : function (_self, e)
41589                                 {
41590                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41591                                     sd.setMonth(sd.getMonth()+1);
41592                                     _this.monthField.setValue(sd.format('Y-m-d'));
41593                                     _this.grid.ds.load({});
41594                                 }
41595                             },
41596                             text : "Next"
41597                         }
41598                     ]
41599                 },
41600                  
41601             }
41602         };
41603         
41604         *//*
41605  * Based on:
41606  * Ext JS Library 1.1.1
41607  * Copyright(c) 2006-2007, Ext JS, LLC.
41608  *
41609  * Originally Released Under LGPL - original licence link has changed is not relivant.
41610  *
41611  * Fork - LGPL
41612  * <script type="text/javascript">
41613  */
41614  
41615 /**
41616  * @class Roo.LoadMask
41617  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41618  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41619  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41620  * element's UpdateManager load indicator and will be destroyed after the initial load.
41621  * @constructor
41622  * Create a new LoadMask
41623  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41624  * @param {Object} config The config object
41625  */
41626 Roo.LoadMask = function(el, config){
41627     this.el = Roo.get(el);
41628     Roo.apply(this, config);
41629     if(this.store){
41630         this.store.on('beforeload', this.onBeforeLoad, this);
41631         this.store.on('load', this.onLoad, this);
41632         this.store.on('loadexception', this.onLoadException, this);
41633         this.removeMask = false;
41634     }else{
41635         var um = this.el.getUpdateManager();
41636         um.showLoadIndicator = false; // disable the default indicator
41637         um.on('beforeupdate', this.onBeforeLoad, this);
41638         um.on('update', this.onLoad, this);
41639         um.on('failure', this.onLoad, this);
41640         this.removeMask = true;
41641     }
41642 };
41643
41644 Roo.LoadMask.prototype = {
41645     /**
41646      * @cfg {Boolean} removeMask
41647      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41648      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41649      */
41650     /**
41651      * @cfg {String} msg
41652      * The text to display in a centered loading message box (defaults to 'Loading...')
41653      */
41654     msg : 'Loading...',
41655     /**
41656      * @cfg {String} msgCls
41657      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41658      */
41659     msgCls : 'x-mask-loading',
41660
41661     /**
41662      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41663      * @type Boolean
41664      */
41665     disabled: false,
41666
41667     /**
41668      * Disables the mask to prevent it from being displayed
41669      */
41670     disable : function(){
41671        this.disabled = true;
41672     },
41673
41674     /**
41675      * Enables the mask so that it can be displayed
41676      */
41677     enable : function(){
41678         this.disabled = false;
41679     },
41680     
41681     onLoadException : function()
41682     {
41683         Roo.log(arguments);
41684         
41685         if (typeof(arguments[3]) != 'undefined') {
41686             Roo.MessageBox.alert("Error loading",arguments[3]);
41687         } 
41688         /*
41689         try {
41690             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41691                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41692             }   
41693         } catch(e) {
41694             
41695         }
41696         */
41697     
41698         
41699         
41700         this.el.unmask(this.removeMask);
41701     },
41702     // private
41703     onLoad : function()
41704     {
41705         this.el.unmask(this.removeMask);
41706     },
41707
41708     // private
41709     onBeforeLoad : function(){
41710         if(!this.disabled){
41711             this.el.mask(this.msg, this.msgCls);
41712         }
41713     },
41714
41715     // private
41716     destroy : function(){
41717         if(this.store){
41718             this.store.un('beforeload', this.onBeforeLoad, this);
41719             this.store.un('load', this.onLoad, this);
41720             this.store.un('loadexception', this.onLoadException, this);
41721         }else{
41722             var um = this.el.getUpdateManager();
41723             um.un('beforeupdate', this.onBeforeLoad, this);
41724             um.un('update', this.onLoad, this);
41725             um.un('failure', this.onLoad, this);
41726         }
41727     }
41728 };/*
41729  * Based on:
41730  * Ext JS Library 1.1.1
41731  * Copyright(c) 2006-2007, Ext JS, LLC.
41732  *
41733  * Originally Released Under LGPL - original licence link has changed is not relivant.
41734  *
41735  * Fork - LGPL
41736  * <script type="text/javascript">
41737  */
41738
41739
41740 /**
41741  * @class Roo.XTemplate
41742  * @extends Roo.Template
41743  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
41744 <pre><code>
41745 var t = new Roo.XTemplate(
41746         '&lt;select name="{name}"&gt;',
41747                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
41748         '&lt;/select&gt;'
41749 );
41750  
41751 // then append, applying the master template values
41752  </code></pre>
41753  *
41754  * Supported features:
41755  *
41756  *  Tags:
41757
41758 <pre><code>
41759       {a_variable} - output encoded.
41760       {a_variable.format:("Y-m-d")} - call a method on the variable
41761       {a_variable:raw} - unencoded output
41762       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
41763       {a_variable:this.method_on_template(...)} - call a method on the template object.
41764  
41765 </code></pre>
41766  *  The tpl tag:
41767 <pre><code>
41768         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
41769         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
41770         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
41771         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
41772   
41773         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
41774         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
41775 </code></pre>
41776  *      
41777  */
41778 Roo.XTemplate = function()
41779 {
41780     Roo.XTemplate.superclass.constructor.apply(this, arguments);
41781     if (this.html) {
41782         this.compile();
41783     }
41784 };
41785
41786
41787 Roo.extend(Roo.XTemplate, Roo.Template, {
41788
41789     /**
41790      * The various sub templates
41791      */
41792     tpls : false,
41793     /**
41794      *
41795      * basic tag replacing syntax
41796      * WORD:WORD()
41797      *
41798      * // you can fake an object call by doing this
41799      *  x.t:(test,tesT) 
41800      * 
41801      */
41802     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
41803
41804     /**
41805      * compile the template
41806      *
41807      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
41808      *
41809      */
41810     compile: function()
41811     {
41812         var s = this.html;
41813      
41814         s = ['<tpl>', s, '</tpl>'].join('');
41815     
41816         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
41817             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
41818             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
41819             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
41820             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
41821             m,
41822             id     = 0,
41823             tpls   = [];
41824     
41825         while(true == !!(m = s.match(re))){
41826             var forMatch   = m[0].match(nameRe),
41827                 ifMatch   = m[0].match(ifRe),
41828                 execMatch   = m[0].match(execRe),
41829                 namedMatch   = m[0].match(namedRe),
41830                 
41831                 exp  = null, 
41832                 fn   = null,
41833                 exec = null,
41834                 name = forMatch && forMatch[1] ? forMatch[1] : '';
41835                 
41836             if (ifMatch) {
41837                 // if - puts fn into test..
41838                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
41839                 if(exp){
41840                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
41841                 }
41842             }
41843             
41844             if (execMatch) {
41845                 // exec - calls a function... returns empty if true is  returned.
41846                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
41847                 if(exp){
41848                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
41849                 }
41850             }
41851             
41852             
41853             if (name) {
41854                 // for = 
41855                 switch(name){
41856                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
41857                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
41858                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
41859                 }
41860             }
41861             var uid = namedMatch ? namedMatch[1] : id;
41862             
41863             
41864             tpls.push({
41865                 id:     namedMatch ? namedMatch[1] : id,
41866                 target: name,
41867                 exec:   exec,
41868                 test:   fn,
41869                 body:   m[1] || ''
41870             });
41871             if (namedMatch) {
41872                 s = s.replace(m[0], '');
41873             } else { 
41874                 s = s.replace(m[0], '{xtpl'+ id + '}');
41875             }
41876             ++id;
41877         }
41878         this.tpls = [];
41879         for(var i = tpls.length-1; i >= 0; --i){
41880             this.compileTpl(tpls[i]);
41881             this.tpls[tpls[i].id] = tpls[i];
41882         }
41883         this.master = tpls[tpls.length-1];
41884         return this;
41885     },
41886     /**
41887      * same as applyTemplate, except it's done to one of the subTemplates
41888      * when using named templates, you can do:
41889      *
41890      * var str = pl.applySubTemplate('your-name', values);
41891      *
41892      * 
41893      * @param {Number} id of the template
41894      * @param {Object} values to apply to template
41895      * @param {Object} parent (normaly the instance of this object)
41896      */
41897     applySubTemplate : function(id, values, parent)
41898     {
41899         
41900         
41901         var t = this.tpls[id];
41902         
41903         
41904         try { 
41905             if(t.test && !t.test.call(this, values, parent)){
41906                 return '';
41907             }
41908         } catch(e) {
41909             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
41910             Roo.log(e.toString());
41911             Roo.log(t.test);
41912             return ''
41913         }
41914         try { 
41915             
41916             if(t.exec && t.exec.call(this, values, parent)){
41917                 return '';
41918             }
41919         } catch(e) {
41920             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
41921             Roo.log(e.toString());
41922             Roo.log(t.exec);
41923             return ''
41924         }
41925         try {
41926             var vs = t.target ? t.target.call(this, values, parent) : values;
41927             parent = t.target ? values : parent;
41928             if(t.target && vs instanceof Array){
41929                 var buf = [];
41930                 for(var i = 0, len = vs.length; i < len; i++){
41931                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
41932                 }
41933                 return buf.join('');
41934             }
41935             return t.compiled.call(this, vs, parent);
41936         } catch (e) {
41937             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
41938             Roo.log(e.toString());
41939             Roo.log(t.compiled);
41940             return '';
41941         }
41942     },
41943
41944     compileTpl : function(tpl)
41945     {
41946         var fm = Roo.util.Format;
41947         var useF = this.disableFormats !== true;
41948         var sep = Roo.isGecko ? "+" : ",";
41949         var undef = function(str) {
41950             Roo.log("Property not found :"  + str);
41951             return '';
41952         };
41953         
41954         var fn = function(m, name, format, args)
41955         {
41956             //Roo.log(arguments);
41957             args = args ? args.replace(/\\'/g,"'") : args;
41958             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
41959             if (typeof(format) == 'undefined') {
41960                 format= 'htmlEncode';
41961             }
41962             if (format == 'raw' ) {
41963                 format = false;
41964             }
41965             
41966             if(name.substr(0, 4) == 'xtpl'){
41967                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
41968             }
41969             
41970             // build an array of options to determine if value is undefined..
41971             
41972             // basically get 'xxxx.yyyy' then do
41973             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
41974             //    (function () { Roo.log("Property not found"); return ''; })() :
41975             //    ......
41976             
41977             var udef_ar = [];
41978             var lookfor = '';
41979             Roo.each(name.split('.'), function(st) {
41980                 lookfor += (lookfor.length ? '.': '') + st;
41981                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
41982             });
41983             
41984             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
41985             
41986             
41987             if(format && useF){
41988                 
41989                 args = args ? ',' + args : "";
41990                  
41991                 if(format.substr(0, 5) != "this."){
41992                     format = "fm." + format + '(';
41993                 }else{
41994                     format = 'this.call("'+ format.substr(5) + '", ';
41995                     args = ", values";
41996                 }
41997                 
41998                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
41999             }
42000              
42001             if (args.length) {
42002                 // called with xxyx.yuu:(test,test)
42003                 // change to ()
42004                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42005             }
42006             // raw.. - :raw modifier..
42007             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42008             
42009         };
42010         var body;
42011         // branched to use + in gecko and [].join() in others
42012         if(Roo.isGecko){
42013             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42014                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42015                     "';};};";
42016         }else{
42017             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42018             body.push(tpl.body.replace(/(\r\n|\n)/g,
42019                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42020             body.push("'].join('');};};");
42021             body = body.join('');
42022         }
42023         
42024         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42025        
42026         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42027         eval(body);
42028         
42029         return this;
42030     },
42031
42032     applyTemplate : function(values){
42033         return this.master.compiled.call(this, values, {});
42034         //var s = this.subs;
42035     },
42036
42037     apply : function(){
42038         return this.applyTemplate.apply(this, arguments);
42039     }
42040
42041  });
42042
42043 Roo.XTemplate.from = function(el){
42044     el = Roo.getDom(el);
42045     return new Roo.XTemplate(el.value || el.innerHTML);
42046 };