roojs-core.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825      
826         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827             //Roo.log('not touch/ button !=0');
828             return;
829         }
830         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831             return; // double touch..
832         }
833         
834
835         if (this.isLocked()) {
836             //Roo.log('locked');
837             return;
838         }
839
840         this.DDM.refreshCache(this.groups);
841 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
844             //Roo.log('no outer handes or not over target');
845                 // do nothing.
846         } else {
847 //            Roo.log('check validator');
848             if (this.clickValidator(e)) {
849 //                Roo.log('validate success');
850                 // set the initial element position
851                 this.setStartPosition();
852
853
854                 this.b4MouseDown(e);
855                 this.onMouseDown(e);
856
857                 this.DDM.handleMouseDown(e, this);
858
859                 this.DDM.stopEvent(e);
860             } else {
861
862
863             }
864         }
865     },
866
867     clickValidator: function(e) {
868         var target = e.getTarget();
869         return ( this.isValidHandleChild(target) &&
870                     (this.id == this.handleElId ||
871                         this.DDM.handleWasClicked(target, this.id)) );
872     },
873
874     /**
875      * Allows you to specify a tag name that should not start a drag operation
876      * when clicked.  This is designed to facilitate embedding links within a
877      * drag handle that do something other than start the drag.
878      * @method addInvalidHandleType
879      * @param {string} tagName the type of element to exclude
880      */
881     addInvalidHandleType: function(tagName) {
882         var type = tagName.toUpperCase();
883         this.invalidHandleTypes[type] = type;
884     },
885
886     /**
887      * Lets you to specify an element id for a child of a drag handle
888      * that should not initiate a drag
889      * @method addInvalidHandleId
890      * @param {string} id the element id of the element you wish to ignore
891      */
892     addInvalidHandleId: function(id) {
893         if (typeof id !== "string") {
894             id = Roo.id(id);
895         }
896         this.invalidHandleIds[id] = id;
897     },
898
899     /**
900      * Lets you specify a css class of elements that will not initiate a drag
901      * @method addInvalidHandleClass
902      * @param {string} cssClass the class of the elements you wish to ignore
903      */
904     addInvalidHandleClass: function(cssClass) {
905         this.invalidHandleClasses.push(cssClass);
906     },
907
908     /**
909      * Unsets an excluded tag name set by addInvalidHandleType
910      * @method removeInvalidHandleType
911      * @param {string} tagName the type of element to unexclude
912      */
913     removeInvalidHandleType: function(tagName) {
914         var type = tagName.toUpperCase();
915         // this.invalidHandleTypes[type] = null;
916         delete this.invalidHandleTypes[type];
917     },
918
919     /**
920      * Unsets an invalid handle id
921      * @method removeInvalidHandleId
922      * @param {string} id the id of the element to re-enable
923      */
924     removeInvalidHandleId: function(id) {
925         if (typeof id !== "string") {
926             id = Roo.id(id);
927         }
928         delete this.invalidHandleIds[id];
929     },
930
931     /**
932      * Unsets an invalid css class
933      * @method removeInvalidHandleClass
934      * @param {string} cssClass the class of the element(s) you wish to
935      * re-enable
936      */
937     removeInvalidHandleClass: function(cssClass) {
938         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939             if (this.invalidHandleClasses[i] == cssClass) {
940                 delete this.invalidHandleClasses[i];
941             }
942         }
943     },
944
945     /**
946      * Checks the tag exclusion list to see if this click should be ignored
947      * @method isValidHandleChild
948      * @param {HTMLElement} node the HTMLElement to evaluate
949      * @return {boolean} true if this is a valid tag type, false if not
950      */
951     isValidHandleChild: function(node) {
952
953         var valid = true;
954         // var n = (node.nodeName == "#text") ? node.parentNode : node;
955         var nodeName;
956         try {
957             nodeName = node.nodeName.toUpperCase();
958         } catch(e) {
959             nodeName = node.nodeName;
960         }
961         valid = valid && !this.invalidHandleTypes[nodeName];
962         valid = valid && !this.invalidHandleIds[node.id];
963
964         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
966         }
967
968
969         return valid;
970
971     },
972
973     /**
974      * Create the array of horizontal tick marks if an interval was specified
975      * in setXConstraint().
976      * @method setXTicks
977      * @private
978      */
979     setXTicks: function(iStartX, iTickSize) {
980         this.xTicks = [];
981         this.xTickSize = iTickSize;
982
983         var tickMap = {};
984
985         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
986             if (!tickMap[i]) {
987                 this.xTicks[this.xTicks.length] = i;
988                 tickMap[i] = true;
989             }
990         }
991
992         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
993             if (!tickMap[i]) {
994                 this.xTicks[this.xTicks.length] = i;
995                 tickMap[i] = true;
996             }
997         }
998
999         this.xTicks.sort(this.DDM.numericSort) ;
1000     },
1001
1002     /**
1003      * Create the array of vertical tick marks if an interval was specified in
1004      * setYConstraint().
1005      * @method setYTicks
1006      * @private
1007      */
1008     setYTicks: function(iStartY, iTickSize) {
1009         this.yTicks = [];
1010         this.yTickSize = iTickSize;
1011
1012         var tickMap = {};
1013
1014         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1015             if (!tickMap[i]) {
1016                 this.yTicks[this.yTicks.length] = i;
1017                 tickMap[i] = true;
1018             }
1019         }
1020
1021         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1022             if (!tickMap[i]) {
1023                 this.yTicks[this.yTicks.length] = i;
1024                 tickMap[i] = true;
1025             }
1026         }
1027
1028         this.yTicks.sort(this.DDM.numericSort) ;
1029     },
1030
1031     /**
1032      * By default, the element can be dragged any place on the screen.  Use
1033      * this method to limit the horizontal travel of the element.  Pass in
1034      * 0,0 for the parameters if you want to lock the drag to the y axis.
1035      * @method setXConstraint
1036      * @param {int} iLeft the number of pixels the element can move to the left
1037      * @param {int} iRight the number of pixels the element can move to the
1038      * right
1039      * @param {int} iTickSize optional parameter for specifying that the
1040      * element
1041      * should move iTickSize pixels at a time.
1042      */
1043     setXConstraint: function(iLeft, iRight, iTickSize) {
1044         this.leftConstraint = iLeft;
1045         this.rightConstraint = iRight;
1046
1047         this.minX = this.initPageX - iLeft;
1048         this.maxX = this.initPageX + iRight;
1049         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1050
1051         this.constrainX = true;
1052     },
1053
1054     /**
1055      * Clears any constraints applied to this instance.  Also clears ticks
1056      * since they can't exist independent of a constraint at this time.
1057      * @method clearConstraints
1058      */
1059     clearConstraints: function() {
1060         this.constrainX = false;
1061         this.constrainY = false;
1062         this.clearTicks();
1063     },
1064
1065     /**
1066      * Clears any tick interval defined for this instance
1067      * @method clearTicks
1068      */
1069     clearTicks: function() {
1070         this.xTicks = null;
1071         this.yTicks = null;
1072         this.xTickSize = 0;
1073         this.yTickSize = 0;
1074     },
1075
1076     /**
1077      * By default, the element can be dragged any place on the screen.  Set
1078      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1079      * parameters if you want to lock the drag to the x axis.
1080      * @method setYConstraint
1081      * @param {int} iUp the number of pixels the element can move up
1082      * @param {int} iDown the number of pixels the element can move down
1083      * @param {int} iTickSize optional parameter for specifying that the
1084      * element should move iTickSize pixels at a time.
1085      */
1086     setYConstraint: function(iUp, iDown, iTickSize) {
1087         this.topConstraint = iUp;
1088         this.bottomConstraint = iDown;
1089
1090         this.minY = this.initPageY - iUp;
1091         this.maxY = this.initPageY + iDown;
1092         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1093
1094         this.constrainY = true;
1095
1096     },
1097
1098     /**
1099      * resetConstraints must be called if you manually reposition a dd element.
1100      * @method resetConstraints
1101      * @param {boolean} maintainOffset
1102      */
1103     resetConstraints: function() {
1104
1105
1106         // Maintain offsets if necessary
1107         if (this.initPageX || this.initPageX === 0) {
1108             // figure out how much this thing has moved
1109             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1111
1112             this.setInitPosition(dx, dy);
1113
1114         // This is the first time we have detected the element's position
1115         } else {
1116             this.setInitPosition();
1117         }
1118
1119         if (this.constrainX) {
1120             this.setXConstraint( this.leftConstraint,
1121                                  this.rightConstraint,
1122                                  this.xTickSize        );
1123         }
1124
1125         if (this.constrainY) {
1126             this.setYConstraint( this.topConstraint,
1127                                  this.bottomConstraint,
1128                                  this.yTickSize         );
1129         }
1130     },
1131
1132     /**
1133      * Normally the drag element is moved pixel by pixel, but we can specify
1134      * that it move a number of pixels at a time.  This method resolves the
1135      * location when we have it set up like this.
1136      * @method getTick
1137      * @param {int} val where we want to place the object
1138      * @param {int[]} tickArray sorted array of valid points
1139      * @return {int} the closest tick
1140      * @private
1141      */
1142     getTick: function(val, tickArray) {
1143
1144         if (!tickArray) {
1145             // If tick interval is not defined, it is effectively 1 pixel,
1146             // so we return the value passed to us.
1147             return val;
1148         } else if (tickArray[0] >= val) {
1149             // The value is lower than the first tick, so we return the first
1150             // tick.
1151             return tickArray[0];
1152         } else {
1153             for (var i=0, len=tickArray.length; i<len; ++i) {
1154                 var next = i + 1;
1155                 if (tickArray[next] && tickArray[next] >= val) {
1156                     var diff1 = val - tickArray[i];
1157                     var diff2 = tickArray[next] - val;
1158                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1159                 }
1160             }
1161
1162             // The value is larger than the last tick, so we return the last
1163             // tick.
1164             return tickArray[tickArray.length - 1];
1165         }
1166     },
1167
1168     /**
1169      * toString method
1170      * @method toString
1171      * @return {string} string representation of the dd obj
1172      */
1173     toString: function() {
1174         return ("DragDrop " + this.id);
1175     }
1176
1177 });
1178
1179 })();
1180 /*
1181  * Based on:
1182  * Ext JS Library 1.1.1
1183  * Copyright(c) 2006-2007, Ext JS, LLC.
1184  *
1185  * Originally Released Under LGPL - original licence link has changed is not relivant.
1186  *
1187  * Fork - LGPL
1188  * <script type="text/javascript">
1189  */
1190
1191
1192 /**
1193  * The drag and drop utility provides a framework for building drag and drop
1194  * applications.  In addition to enabling drag and drop for specific elements,
1195  * the drag and drop elements are tracked by the manager class, and the
1196  * interactions between the various elements are tracked during the drag and
1197  * the implementing code is notified about these important moments.
1198  */
1199
1200 // Only load the library once.  Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1203
1204 /**
1205  * @class Roo.dd.DragDropMgr
1206  * DragDropMgr is a singleton that tracks the element interaction for
1207  * all DragDrop items in the window.  Generally, you will not call
1208  * this class directly, but it does have helper methods that could
1209  * be useful in your DragDrop implementations.
1210  * @singleton
1211  */
1212 Roo.dd.DragDropMgr = function() {
1213
1214     var Event = Roo.EventManager;
1215
1216     return {
1217
1218         /**
1219          * Two dimensional Array of registered DragDrop objects.  The first
1220          * dimension is the DragDrop item group, the second the DragDrop
1221          * object.
1222          * @property ids
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         ids: {},
1228
1229         /**
1230          * Array of element ids defined as drag handles.  Used to determine
1231          * if the element that generated the mousedown event is actually the
1232          * handle and not the html element itself.
1233          * @property handleIds
1234          * @type {string: string}
1235          * @private
1236          * @static
1237          */
1238         handleIds: {},
1239
1240         /**
1241          * the DragDrop object that is currently being dragged
1242          * @property dragCurrent
1243          * @type DragDrop
1244          * @private
1245          * @static
1246          **/
1247         dragCurrent: null,
1248
1249         /**
1250          * the DragDrop object(s) that are being hovered over
1251          * @property dragOvers
1252          * @type Array
1253          * @private
1254          * @static
1255          */
1256         dragOvers: {},
1257
1258         /**
1259          * the X distance between the cursor and the object being dragged
1260          * @property deltaX
1261          * @type int
1262          * @private
1263          * @static
1264          */
1265         deltaX: 0,
1266
1267         /**
1268          * the Y distance between the cursor and the object being dragged
1269          * @property deltaY
1270          * @type int
1271          * @private
1272          * @static
1273          */
1274         deltaY: 0,
1275
1276         /**
1277          * Flag to determine if we should prevent the default behavior of the
1278          * events we define. By default this is true, but this can be set to
1279          * false if you need the default behavior (not recommended)
1280          * @property preventDefault
1281          * @type boolean
1282          * @static
1283          */
1284         preventDefault: true,
1285
1286         /**
1287          * Flag to determine if we should stop the propagation of the events
1288          * we generate. This is true by default but you may want to set it to
1289          * false if the html element contains other features that require the
1290          * mouse click.
1291          * @property stopPropagation
1292          * @type boolean
1293          * @static
1294          */
1295         stopPropagation: true,
1296
1297         /**
1298          * Internal flag that is set to true when drag and drop has been
1299          * intialized
1300          * @property initialized
1301          * @private
1302          * @static
1303          */
1304         initalized: false,
1305
1306         /**
1307          * All drag and drop can be disabled.
1308          * @property locked
1309          * @private
1310          * @static
1311          */
1312         locked: false,
1313
1314         /**
1315          * Called the first time an element is registered.
1316          * @method init
1317          * @private
1318          * @static
1319          */
1320         init: function() {
1321             this.initialized = true;
1322         },
1323
1324         /**
1325          * In point mode, drag and drop interaction is defined by the
1326          * location of the cursor during the drag/drop
1327          * @property POINT
1328          * @type int
1329          * @static
1330          */
1331         POINT: 0,
1332
1333         /**
1334          * In intersect mode, drag and drop interactio nis defined by the
1335          * overlap of two or more drag and drop objects.
1336          * @property INTERSECT
1337          * @type int
1338          * @static
1339          */
1340         INTERSECT: 1,
1341
1342         /**
1343          * The current drag and drop mode.  Default: POINT
1344          * @property mode
1345          * @type int
1346          * @static
1347          */
1348         mode: 0,
1349
1350         /**
1351          * Runs method on all drag and drop objects
1352          * @method _execOnAll
1353          * @private
1354          * @static
1355          */
1356         _execOnAll: function(sMethod, args) {
1357             for (var i in this.ids) {
1358                 for (var j in this.ids[i]) {
1359                     var oDD = this.ids[i][j];
1360                     if (! this.isTypeOfDD(oDD)) {
1361                         continue;
1362                     }
1363                     oDD[sMethod].apply(oDD, args);
1364                 }
1365             }
1366         },
1367
1368         /**
1369          * Drag and drop initialization.  Sets up the global event handlers
1370          * @method _onLoad
1371          * @private
1372          * @static
1373          */
1374         _onLoad: function() {
1375
1376             this.init();
1377
1378             if (!Roo.isTouch) {
1379                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1380                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1381             }
1382             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1383             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1384             
1385             Event.on(window,   "unload",    this._onUnload, this, true);
1386             Event.on(window,   "resize",    this._onResize, this, true);
1387             // Event.on(window,   "mouseout",    this._test);
1388
1389         },
1390
1391         /**
1392          * Reset constraints on all drag and drop objs
1393          * @method _onResize
1394          * @private
1395          * @static
1396          */
1397         _onResize: function(e) {
1398             this._execOnAll("resetConstraints", []);
1399         },
1400
1401         /**
1402          * Lock all drag and drop functionality
1403          * @method lock
1404          * @static
1405          */
1406         lock: function() { this.locked = true; },
1407
1408         /**
1409          * Unlock all drag and drop functionality
1410          * @method unlock
1411          * @static
1412          */
1413         unlock: function() { this.locked = false; },
1414
1415         /**
1416          * Is drag and drop locked?
1417          * @method isLocked
1418          * @return {boolean} True if drag and drop is locked, false otherwise.
1419          * @static
1420          */
1421         isLocked: function() { return this.locked; },
1422
1423         /**
1424          * Location cache that is set for all drag drop objects when a drag is
1425          * initiated, cleared when the drag is finished.
1426          * @property locationCache
1427          * @private
1428          * @static
1429          */
1430         locationCache: {},
1431
1432         /**
1433          * Set useCache to false if you want to force object the lookup of each
1434          * drag and drop linked element constantly during a drag.
1435          * @property useCache
1436          * @type boolean
1437          * @static
1438          */
1439         useCache: true,
1440
1441         /**
1442          * The number of pixels that the mouse needs to move after the
1443          * mousedown before the drag is initiated.  Default=3;
1444          * @property clickPixelThresh
1445          * @type int
1446          * @static
1447          */
1448         clickPixelThresh: 3,
1449
1450         /**
1451          * The number of milliseconds after the mousedown event to initiate the
1452          * drag if we don't get a mouseup event. Default=1000
1453          * @property clickTimeThresh
1454          * @type int
1455          * @static
1456          */
1457         clickTimeThresh: 350,
1458
1459         /**
1460          * Flag that indicates that either the drag pixel threshold or the
1461          * mousdown time threshold has been met
1462          * @property dragThreshMet
1463          * @type boolean
1464          * @private
1465          * @static
1466          */
1467         dragThreshMet: false,
1468
1469         /**
1470          * Timeout used for the click time threshold
1471          * @property clickTimeout
1472          * @type Object
1473          * @private
1474          * @static
1475          */
1476         clickTimeout: null,
1477
1478         /**
1479          * The X position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startX
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startX: 0,
1487
1488         /**
1489          * The Y position of the mousedown event stored for later use when a
1490          * drag threshold is met.
1491          * @property startY
1492          * @type int
1493          * @private
1494          * @static
1495          */
1496         startY: 0,
1497
1498         /**
1499          * Each DragDrop instance must be registered with the DragDropMgr.
1500          * This is executed in DragDrop.init()
1501          * @method regDragDrop
1502          * @param {DragDrop} oDD the DragDrop object to register
1503          * @param {String} sGroup the name of the group this element belongs to
1504          * @static
1505          */
1506         regDragDrop: function(oDD, sGroup) {
1507             if (!this.initialized) { this.init(); }
1508
1509             if (!this.ids[sGroup]) {
1510                 this.ids[sGroup] = {};
1511             }
1512             this.ids[sGroup][oDD.id] = oDD;
1513         },
1514
1515         /**
1516          * Removes the supplied dd instance from the supplied group. Executed
1517          * by DragDrop.removeFromGroup, so don't call this function directly.
1518          * @method removeDDFromGroup
1519          * @private
1520          * @static
1521          */
1522         removeDDFromGroup: function(oDD, sGroup) {
1523             if (!this.ids[sGroup]) {
1524                 this.ids[sGroup] = {};
1525             }
1526
1527             var obj = this.ids[sGroup];
1528             if (obj && obj[oDD.id]) {
1529                 delete obj[oDD.id];
1530             }
1531         },
1532
1533         /**
1534          * Unregisters a drag and drop item.  This is executed in
1535          * DragDrop.unreg, use that method instead of calling this directly.
1536          * @method _remove
1537          * @private
1538          * @static
1539          */
1540         _remove: function(oDD) {
1541             for (var g in oDD.groups) {
1542                 if (g && this.ids[g][oDD.id]) {
1543                     delete this.ids[g][oDD.id];
1544                 }
1545             }
1546             delete this.handleIds[oDD.id];
1547         },
1548
1549         /**
1550          * Each DragDrop handle element must be registered.  This is done
1551          * automatically when executing DragDrop.setHandleElId()
1552          * @method regHandle
1553          * @param {String} sDDId the DragDrop id this element is a handle for
1554          * @param {String} sHandleId the id of the element that is the drag
1555          * handle
1556          * @static
1557          */
1558         regHandle: function(sDDId, sHandleId) {
1559             if (!this.handleIds[sDDId]) {
1560                 this.handleIds[sDDId] = {};
1561             }
1562             this.handleIds[sDDId][sHandleId] = sHandleId;
1563         },
1564
1565         /**
1566          * Utility function to determine if a given element has been
1567          * registered as a drag drop item.
1568          * @method isDragDrop
1569          * @param {String} id the element id to check
1570          * @return {boolean} true if this element is a DragDrop item,
1571          * false otherwise
1572          * @static
1573          */
1574         isDragDrop: function(id) {
1575             return ( this.getDDById(id) ) ? true : false;
1576         },
1577
1578         /**
1579          * Returns the drag and drop instances that are in all groups the
1580          * passed in instance belongs to.
1581          * @method getRelated
1582          * @param {DragDrop} p_oDD the obj to get related data for
1583          * @param {boolean} bTargetsOnly if true, only return targetable objs
1584          * @return {DragDrop[]} the related instances
1585          * @static
1586          */
1587         getRelated: function(p_oDD, bTargetsOnly) {
1588             var oDDs = [];
1589             for (var i in p_oDD.groups) {
1590                 for (j in this.ids[i]) {
1591                     var dd = this.ids[i][j];
1592                     if (! this.isTypeOfDD(dd)) {
1593                         continue;
1594                     }
1595                     if (!bTargetsOnly || dd.isTarget) {
1596                         oDDs[oDDs.length] = dd;
1597                     }
1598                 }
1599             }
1600
1601             return oDDs;
1602         },
1603
1604         /**
1605          * Returns true if the specified dd target is a legal target for
1606          * the specifice drag obj
1607          * @method isLegalTarget
1608          * @param {DragDrop} the drag obj
1609          * @param {DragDrop} the target
1610          * @return {boolean} true if the target is a legal target for the
1611          * dd obj
1612          * @static
1613          */
1614         isLegalTarget: function (oDD, oTargetDD) {
1615             var targets = this.getRelated(oDD, true);
1616             for (var i=0, len=targets.length;i<len;++i) {
1617                 if (targets[i].id == oTargetDD.id) {
1618                     return true;
1619                 }
1620             }
1621
1622             return false;
1623         },
1624
1625         /**
1626          * My goal is to be able to transparently determine if an object is
1627          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1628          * returns "object", oDD.constructor.toString() always returns
1629          * "DragDrop" and not the name of the subclass.  So for now it just
1630          * evaluates a well-known variable in DragDrop.
1631          * @method isTypeOfDD
1632          * @param {Object} the object to evaluate
1633          * @return {boolean} true if typeof oDD = DragDrop
1634          * @static
1635          */
1636         isTypeOfDD: function (oDD) {
1637             return (oDD && oDD.__ygDragDrop);
1638         },
1639
1640         /**
1641          * Utility function to determine if a given element has been
1642          * registered as a drag drop handle for the given Drag Drop object.
1643          * @method isHandle
1644          * @param {String} id the element id to check
1645          * @return {boolean} true if this element is a DragDrop handle, false
1646          * otherwise
1647          * @static
1648          */
1649         isHandle: function(sDDId, sHandleId) {
1650             return ( this.handleIds[sDDId] &&
1651                             this.handleIds[sDDId][sHandleId] );
1652         },
1653
1654         /**
1655          * Returns the DragDrop instance for a given id
1656          * @method getDDById
1657          * @param {String} id the id of the DragDrop object
1658          * @return {DragDrop} the drag drop object, null if it is not found
1659          * @static
1660          */
1661         getDDById: function(id) {
1662             for (var i in this.ids) {
1663                 if (this.ids[i][id]) {
1664                     return this.ids[i][id];
1665                 }
1666             }
1667             return null;
1668         },
1669
1670         /**
1671          * Fired after a registered DragDrop object gets the mousedown event.
1672          * Sets up the events required to track the object being dragged
1673          * @method handleMouseDown
1674          * @param {Event} e the event
1675          * @param oDD the DragDrop object being dragged
1676          * @private
1677          * @static
1678          */
1679         handleMouseDown: function(e, oDD) {
1680             if(Roo.QuickTips){
1681                 Roo.QuickTips.disable();
1682             }
1683             this.currentTarget = e.getTarget();
1684
1685             this.dragCurrent = oDD;
1686
1687             var el = oDD.getEl();
1688
1689             // track start position
1690             this.startX = e.getPageX();
1691             this.startY = e.getPageY();
1692
1693             this.deltaX = this.startX - el.offsetLeft;
1694             this.deltaY = this.startY - el.offsetTop;
1695
1696             this.dragThreshMet = false;
1697
1698             this.clickTimeout = setTimeout(
1699                     function() {
1700                         var DDM = Roo.dd.DDM;
1701                         DDM.startDrag(DDM.startX, DDM.startY);
1702                     },
1703                     this.clickTimeThresh );
1704         },
1705
1706         /**
1707          * Fired when either the drag pixel threshol or the mousedown hold
1708          * time threshold has been met.
1709          * @method startDrag
1710          * @param x {int} the X position of the original mousedown
1711          * @param y {int} the Y position of the original mousedown
1712          * @static
1713          */
1714         startDrag: function(x, y) {
1715             clearTimeout(this.clickTimeout);
1716             if (this.dragCurrent) {
1717                 this.dragCurrent.b4StartDrag(x, y);
1718                 this.dragCurrent.startDrag(x, y);
1719             }
1720             this.dragThreshMet = true;
1721         },
1722
1723         /**
1724          * Internal function to handle the mouseup event.  Will be invoked
1725          * from the context of the document.
1726          * @method handleMouseUp
1727          * @param {Event} e the event
1728          * @private
1729          * @static
1730          */
1731         handleMouseUp: function(e) {
1732
1733             if(Roo.QuickTips){
1734                 Roo.QuickTips.enable();
1735             }
1736             if (! this.dragCurrent) {
1737                 return;
1738             }
1739
1740             clearTimeout(this.clickTimeout);
1741
1742             if (this.dragThreshMet) {
1743                 this.fireEvents(e, true);
1744             } else {
1745             }
1746
1747             this.stopDrag(e);
1748
1749             this.stopEvent(e);
1750         },
1751
1752         /**
1753          * Utility to stop event propagation and event default, if these
1754          * features are turned on.
1755          * @method stopEvent
1756          * @param {Event} e the event as returned by this.getEvent()
1757          * @static
1758          */
1759         stopEvent: function(e){
1760             if(this.stopPropagation) {
1761                 e.stopPropagation();
1762             }
1763
1764             if (this.preventDefault) {
1765                 e.preventDefault();
1766             }
1767         },
1768
1769         /**
1770          * Internal function to clean up event handlers after the drag
1771          * operation is complete
1772          * @method stopDrag
1773          * @param {Event} e the event
1774          * @private
1775          * @static
1776          */
1777         stopDrag: function(e) {
1778             // Fire the drag end event for the item that was dragged
1779             if (this.dragCurrent) {
1780                 if (this.dragThreshMet) {
1781                     this.dragCurrent.b4EndDrag(e);
1782                     this.dragCurrent.endDrag(e);
1783                 }
1784
1785                 this.dragCurrent.onMouseUp(e);
1786             }
1787
1788             this.dragCurrent = null;
1789             this.dragOvers = {};
1790         },
1791
1792         /**
1793          * Internal function to handle the mousemove event.  Will be invoked
1794          * from the context of the html element.
1795          *
1796          * @TODO figure out what we can do about mouse events lost when the
1797          * user drags objects beyond the window boundary.  Currently we can
1798          * detect this in internet explorer by verifying that the mouse is
1799          * down during the mousemove event.  Firefox doesn't give us the
1800          * button state on the mousemove event.
1801          * @method handleMouseMove
1802          * @param {Event} e the event
1803          * @private
1804          * @static
1805          */
1806         handleMouseMove: function(e) {
1807             if (! this.dragCurrent) {
1808                 return true;
1809             }
1810
1811             // var button = e.which || e.button;
1812
1813             // check for IE mouseup outside of page boundary
1814             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1815                 this.stopEvent(e);
1816                 return this.handleMouseUp(e);
1817             }
1818
1819             if (!this.dragThreshMet) {
1820                 var diffX = Math.abs(this.startX - e.getPageX());
1821                 var diffY = Math.abs(this.startY - e.getPageY());
1822                 if (diffX > this.clickPixelThresh ||
1823                             diffY > this.clickPixelThresh) {
1824                     this.startDrag(this.startX, this.startY);
1825                 }
1826             }
1827
1828             if (this.dragThreshMet) {
1829                 this.dragCurrent.b4Drag(e);
1830                 this.dragCurrent.onDrag(e);
1831                 if(!this.dragCurrent.moveOnly){
1832                     this.fireEvents(e, false);
1833                 }
1834             }
1835
1836             this.stopEvent(e);
1837
1838             return true;
1839         },
1840
1841         /**
1842          * Iterates over all of the DragDrop elements to find ones we are
1843          * hovering over or dropping on
1844          * @method fireEvents
1845          * @param {Event} e the event
1846          * @param {boolean} isDrop is this a drop op or a mouseover op?
1847          * @private
1848          * @static
1849          */
1850         fireEvents: function(e, isDrop) {
1851             var dc = this.dragCurrent;
1852
1853             // If the user did the mouse up outside of the window, we could
1854             // get here even though we have ended the drag.
1855             if (!dc || dc.isLocked()) {
1856                 return;
1857             }
1858
1859             var pt = e.getPoint();
1860
1861             // cache the previous dragOver array
1862             var oldOvers = [];
1863
1864             var outEvts   = [];
1865             var overEvts  = [];
1866             var dropEvts  = [];
1867             var enterEvts = [];
1868
1869             // Check to see if the object(s) we were hovering over is no longer
1870             // being hovered over so we can fire the onDragOut event
1871             for (var i in this.dragOvers) {
1872
1873                 var ddo = this.dragOvers[i];
1874
1875                 if (! this.isTypeOfDD(ddo)) {
1876                     continue;
1877                 }
1878
1879                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880                     outEvts.push( ddo );
1881                 }
1882
1883                 oldOvers[i] = true;
1884                 delete this.dragOvers[i];
1885             }
1886
1887             for (var sGroup in dc.groups) {
1888
1889                 if ("string" != typeof sGroup) {
1890                     continue;
1891                 }
1892
1893                 for (i in this.ids[sGroup]) {
1894                     var oDD = this.ids[sGroup][i];
1895                     if (! this.isTypeOfDD(oDD)) {
1896                         continue;
1897                     }
1898
1899                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900                         if (this.isOverTarget(pt, oDD, this.mode)) {
1901                             // look for drop interactions
1902                             if (isDrop) {
1903                                 dropEvts.push( oDD );
1904                             // look for drag enter and drag over interactions
1905                             } else {
1906
1907                                 // initial drag over: dragEnter fires
1908                                 if (!oldOvers[oDD.id]) {
1909                                     enterEvts.push( oDD );
1910                                 // subsequent drag overs: dragOver fires
1911                                 } else {
1912                                     overEvts.push( oDD );
1913                                 }
1914
1915                                 this.dragOvers[oDD.id] = oDD;
1916                             }
1917                         }
1918                     }
1919                 }
1920             }
1921
1922             if (this.mode) {
1923                 if (outEvts.length) {
1924                     dc.b4DragOut(e, outEvts);
1925                     dc.onDragOut(e, outEvts);
1926                 }
1927
1928                 if (enterEvts.length) {
1929                     dc.onDragEnter(e, enterEvts);
1930                 }
1931
1932                 if (overEvts.length) {
1933                     dc.b4DragOver(e, overEvts);
1934                     dc.onDragOver(e, overEvts);
1935                 }
1936
1937                 if (dropEvts.length) {
1938                     dc.b4DragDrop(e, dropEvts);
1939                     dc.onDragDrop(e, dropEvts);
1940                 }
1941
1942             } else {
1943                 // fire dragout events
1944                 var len = 0;
1945                 for (i=0, len=outEvts.length; i<len; ++i) {
1946                     dc.b4DragOut(e, outEvts[i].id);
1947                     dc.onDragOut(e, outEvts[i].id);
1948                 }
1949
1950                 // fire enter events
1951                 for (i=0,len=enterEvts.length; i<len; ++i) {
1952                     // dc.b4DragEnter(e, oDD.id);
1953                     dc.onDragEnter(e, enterEvts[i].id);
1954                 }
1955
1956                 // fire over events
1957                 for (i=0,len=overEvts.length; i<len; ++i) {
1958                     dc.b4DragOver(e, overEvts[i].id);
1959                     dc.onDragOver(e, overEvts[i].id);
1960                 }
1961
1962                 // fire drop events
1963                 for (i=0, len=dropEvts.length; i<len; ++i) {
1964                     dc.b4DragDrop(e, dropEvts[i].id);
1965                     dc.onDragDrop(e, dropEvts[i].id);
1966                 }
1967
1968             }
1969
1970             // notify about a drop that did not find a target
1971             if (isDrop && !dropEvts.length) {
1972                 dc.onInvalidDrop(e);
1973             }
1974
1975         },
1976
1977         /**
1978          * Helper function for getting the best match from the list of drag
1979          * and drop objects returned by the drag and drop events when we are
1980          * in INTERSECT mode.  It returns either the first object that the
1981          * cursor is over, or the object that has the greatest overlap with
1982          * the dragged element.
1983          * @method getBestMatch
1984          * @param  {DragDrop[]} dds The array of drag and drop objects
1985          * targeted
1986          * @return {DragDrop}       The best single match
1987          * @static
1988          */
1989         getBestMatch: function(dds) {
1990             var winner = null;
1991             // Return null if the input is not what we expect
1992             //if (!dds || !dds.length || dds.length == 0) {
1993                // winner = null;
1994             // If there is only one item, it wins
1995             //} else if (dds.length == 1) {
1996
1997             var len = dds.length;
1998
1999             if (len == 1) {
2000                 winner = dds[0];
2001             } else {
2002                 // Loop through the targeted items
2003                 for (var i=0; i<len; ++i) {
2004                     var dd = dds[i];
2005                     // If the cursor is over the object, it wins.  If the
2006                     // cursor is over multiple matches, the first one we come
2007                     // to wins.
2008                     if (dd.cursorIsOver) {
2009                         winner = dd;
2010                         break;
2011                     // Otherwise the object with the most overlap wins
2012                     } else {
2013                         if (!winner ||
2014                             winner.overlap.getArea() < dd.overlap.getArea()) {
2015                             winner = dd;
2016                         }
2017                     }
2018                 }
2019             }
2020
2021             return winner;
2022         },
2023
2024         /**
2025          * Refreshes the cache of the top-left and bottom-right points of the
2026          * drag and drop objects in the specified group(s).  This is in the
2027          * format that is stored in the drag and drop instance, so typical
2028          * usage is:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2031          * </code>
2032          * Alternatively:
2033          * <code>
2034          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2035          * </code>
2036          * @TODO this really should be an indexed array.  Alternatively this
2037          * method could accept both.
2038          * @method refreshCache
2039          * @param {Object} groups an associative array of groups to refresh
2040          * @static
2041          */
2042         refreshCache: function(groups) {
2043             for (var sGroup in groups) {
2044                 if ("string" != typeof sGroup) {
2045                     continue;
2046                 }
2047                 for (var i in this.ids[sGroup]) {
2048                     var oDD = this.ids[sGroup][i];
2049
2050                     if (this.isTypeOfDD(oDD)) {
2051                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052                         var loc = this.getLocation(oDD);
2053                         if (loc) {
2054                             this.locationCache[oDD.id] = loc;
2055                         } else {
2056                             delete this.locationCache[oDD.id];
2057                             // this will unregister the drag and drop object if
2058                             // the element is not in a usable state
2059                             // oDD.unreg();
2060                         }
2061                     }
2062                 }
2063             }
2064         },
2065
2066         /**
2067          * This checks to make sure an element exists and is in the DOM.  The
2068          * main purpose is to handle cases where innerHTML is used to remove
2069          * drag and drop objects from the DOM.  IE provides an 'unspecified
2070          * error' when trying to access the offsetParent of such an element
2071          * @method verifyEl
2072          * @param {HTMLElement} el the element to check
2073          * @return {boolean} true if the element looks usable
2074          * @static
2075          */
2076         verifyEl: function(el) {
2077             if (el) {
2078                 var parent;
2079                 if(Roo.isIE){
2080                     try{
2081                         parent = el.offsetParent;
2082                     }catch(e){}
2083                 }else{
2084                     parent = el.offsetParent;
2085                 }
2086                 if (parent) {
2087                     return true;
2088                 }
2089             }
2090
2091             return false;
2092         },
2093
2094         /**
2095          * Returns a Region object containing the drag and drop element's position
2096          * and size, including the padding configured for it
2097          * @method getLocation
2098          * @param {DragDrop} oDD the drag and drop object to get the
2099          *                       location for
2100          * @return {Roo.lib.Region} a Region object representing the total area
2101          *                             the element occupies, including any padding
2102          *                             the instance is configured for.
2103          * @static
2104          */
2105         getLocation: function(oDD) {
2106             if (! this.isTypeOfDD(oDD)) {
2107                 return null;
2108             }
2109
2110             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2111
2112             try {
2113                 pos= Roo.lib.Dom.getXY(el);
2114             } catch (e) { }
2115
2116             if (!pos) {
2117                 return null;
2118             }
2119
2120             x1 = pos[0];
2121             x2 = x1 + el.offsetWidth;
2122             y1 = pos[1];
2123             y2 = y1 + el.offsetHeight;
2124
2125             t = y1 - oDD.padding[0];
2126             r = x2 + oDD.padding[1];
2127             b = y2 + oDD.padding[2];
2128             l = x1 - oDD.padding[3];
2129
2130             return new Roo.lib.Region( t, r, b, l );
2131         },
2132
2133         /**
2134          * Checks the cursor location to see if it over the target
2135          * @method isOverTarget
2136          * @param {Roo.lib.Point} pt The point to evaluate
2137          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138          * @return {boolean} true if the mouse is over the target
2139          * @private
2140          * @static
2141          */
2142         isOverTarget: function(pt, oTarget, intersect) {
2143             // use cache if available
2144             var loc = this.locationCache[oTarget.id];
2145             if (!loc || !this.useCache) {
2146                 loc = this.getLocation(oTarget);
2147                 this.locationCache[oTarget.id] = loc;
2148
2149             }
2150
2151             if (!loc) {
2152                 return false;
2153             }
2154
2155             oTarget.cursorIsOver = loc.contains( pt );
2156
2157             // DragDrop is using this as a sanity check for the initial mousedown
2158             // in this case we are done.  In POINT mode, if the drag obj has no
2159             // contraints, we are also done. Otherwise we need to evaluate the
2160             // location of the target as related to the actual location of the
2161             // dragged element.
2162             var dc = this.dragCurrent;
2163             if (!dc || !dc.getTargetCoord ||
2164                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2165                 return oTarget.cursorIsOver;
2166             }
2167
2168             oTarget.overlap = null;
2169
2170             // Get the current location of the drag element, this is the
2171             // location of the mouse event less the delta that represents
2172             // where the original mousedown happened on the element.  We
2173             // need to consider constraints and ticks as well.
2174             var pos = dc.getTargetCoord(pt.x, pt.y);
2175
2176             var el = dc.getDragEl();
2177             var curRegion = new Roo.lib.Region( pos.y,
2178                                                    pos.x + el.offsetWidth,
2179                                                    pos.y + el.offsetHeight,
2180                                                    pos.x );
2181
2182             var overlap = curRegion.intersect(loc);
2183
2184             if (overlap) {
2185                 oTarget.overlap = overlap;
2186                 return (intersect) ? true : oTarget.cursorIsOver;
2187             } else {
2188                 return false;
2189             }
2190         },
2191
2192         /**
2193          * unload event handler
2194          * @method _onUnload
2195          * @private
2196          * @static
2197          */
2198         _onUnload: function(e, me) {
2199             Roo.dd.DragDropMgr.unregAll();
2200         },
2201
2202         /**
2203          * Cleans up the drag and drop events and objects.
2204          * @method unregAll
2205          * @private
2206          * @static
2207          */
2208         unregAll: function() {
2209
2210             if (this.dragCurrent) {
2211                 this.stopDrag();
2212                 this.dragCurrent = null;
2213             }
2214
2215             this._execOnAll("unreg", []);
2216
2217             for (i in this.elementCache) {
2218                 delete this.elementCache[i];
2219             }
2220
2221             this.elementCache = {};
2222             this.ids = {};
2223         },
2224
2225         /**
2226          * A cache of DOM elements
2227          * @property elementCache
2228          * @private
2229          * @static
2230          */
2231         elementCache: {},
2232
2233         /**
2234          * Get the wrapper for the DOM element specified
2235          * @method getElWrapper
2236          * @param {String} id the id of the element to get
2237          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2238          * @private
2239          * @deprecated This wrapper isn't that useful
2240          * @static
2241          */
2242         getElWrapper: function(id) {
2243             var oWrapper = this.elementCache[id];
2244             if (!oWrapper || !oWrapper.el) {
2245                 oWrapper = this.elementCache[id] =
2246                     new this.ElementWrapper(Roo.getDom(id));
2247             }
2248             return oWrapper;
2249         },
2250
2251         /**
2252          * Returns the actual DOM element
2253          * @method getElement
2254          * @param {String} id the id of the elment to get
2255          * @return {Object} The element
2256          * @deprecated use Roo.getDom instead
2257          * @static
2258          */
2259         getElement: function(id) {
2260             return Roo.getDom(id);
2261         },
2262
2263         /**
2264          * Returns the style property for the DOM element (i.e.,
2265          * document.getElById(id).style)
2266          * @method getCss
2267          * @param {String} id the id of the elment to get
2268          * @return {Object} The style property of the element
2269          * @deprecated use Roo.getDom instead
2270          * @static
2271          */
2272         getCss: function(id) {
2273             var el = Roo.getDom(id);
2274             return (el) ? el.style : null;
2275         },
2276
2277         /**
2278          * Inner class for cached elements
2279          * @class DragDropMgr.ElementWrapper
2280          * @for DragDropMgr
2281          * @private
2282          * @deprecated
2283          */
2284         ElementWrapper: function(el) {
2285                 /**
2286                  * The element
2287                  * @property el
2288                  */
2289                 this.el = el || null;
2290                 /**
2291                  * The element id
2292                  * @property id
2293                  */
2294                 this.id = this.el && el.id;
2295                 /**
2296                  * A reference to the style property
2297                  * @property css
2298                  */
2299                 this.css = this.el && el.style;
2300             },
2301
2302         /**
2303          * Returns the X position of an html element
2304          * @method getPosX
2305          * @param el the element for which to get the position
2306          * @return {int} the X coordinate
2307          * @for DragDropMgr
2308          * @deprecated use Roo.lib.Dom.getX instead
2309          * @static
2310          */
2311         getPosX: function(el) {
2312             return Roo.lib.Dom.getX(el);
2313         },
2314
2315         /**
2316          * Returns the Y position of an html element
2317          * @method getPosY
2318          * @param el the element for which to get the position
2319          * @return {int} the Y coordinate
2320          * @deprecated use Roo.lib.Dom.getY instead
2321          * @static
2322          */
2323         getPosY: function(el) {
2324             return Roo.lib.Dom.getY(el);
2325         },
2326
2327         /**
2328          * Swap two nodes.  In IE, we use the native method, for others we
2329          * emulate the IE behavior
2330          * @method swapNode
2331          * @param n1 the first node to swap
2332          * @param n2 the other node to swap
2333          * @static
2334          */
2335         swapNode: function(n1, n2) {
2336             if (n1.swapNode) {
2337                 n1.swapNode(n2);
2338             } else {
2339                 var p = n2.parentNode;
2340                 var s = n2.nextSibling;
2341
2342                 if (s == n1) {
2343                     p.insertBefore(n1, n2);
2344                 } else if (n2 == n1.nextSibling) {
2345                     p.insertBefore(n2, n1);
2346                 } else {
2347                     n1.parentNode.replaceChild(n2, n1);
2348                     p.insertBefore(n1, s);
2349                 }
2350             }
2351         },
2352
2353         /**
2354          * Returns the current scroll position
2355          * @method getScroll
2356          * @private
2357          * @static
2358          */
2359         getScroll: function () {
2360             var t, l, dde=document.documentElement, db=document.body;
2361             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2362                 t = dde.scrollTop;
2363                 l = dde.scrollLeft;
2364             } else if (db) {
2365                 t = db.scrollTop;
2366                 l = db.scrollLeft;
2367             } else {
2368
2369             }
2370             return { top: t, left: l };
2371         },
2372
2373         /**
2374          * Returns the specified element style property
2375          * @method getStyle
2376          * @param {HTMLElement} el          the element
2377          * @param {string}      styleProp   the style property
2378          * @return {string} The value of the style property
2379          * @deprecated use Roo.lib.Dom.getStyle
2380          * @static
2381          */
2382         getStyle: function(el, styleProp) {
2383             return Roo.fly(el).getStyle(styleProp);
2384         },
2385
2386         /**
2387          * Gets the scrollTop
2388          * @method getScrollTop
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollTop: function () { return this.getScroll().top; },
2393
2394         /**
2395          * Gets the scrollLeft
2396          * @method getScrollLeft
2397          * @return {int} the document's scrollTop
2398          * @static
2399          */
2400         getScrollLeft: function () { return this.getScroll().left; },
2401
2402         /**
2403          * Sets the x/y position of an element to the location of the
2404          * target element.
2405          * @method moveToEl
2406          * @param {HTMLElement} moveEl      The element to move
2407          * @param {HTMLElement} targetEl    The position reference element
2408          * @static
2409          */
2410         moveToEl: function (moveEl, targetEl) {
2411             var aCoord = Roo.lib.Dom.getXY(targetEl);
2412             Roo.lib.Dom.setXY(moveEl, aCoord);
2413         },
2414
2415         /**
2416          * Numeric array sort function
2417          * @method numericSort
2418          * @static
2419          */
2420         numericSort: function(a, b) { return (a - b); },
2421
2422         /**
2423          * Internal counter
2424          * @property _timeoutCount
2425          * @private
2426          * @static
2427          */
2428         _timeoutCount: 0,
2429
2430         /**
2431          * Trying to make the load order less important.  Without this we get
2432          * an error if this file is loaded before the Event Utility.
2433          * @method _addListeners
2434          * @private
2435          * @static
2436          */
2437         _addListeners: function() {
2438             var DDM = Roo.dd.DDM;
2439             if ( Roo.lib.Event && document ) {
2440                 DDM._onLoad();
2441             } else {
2442                 if (DDM._timeoutCount > 2000) {
2443                 } else {
2444                     setTimeout(DDM._addListeners, 10);
2445                     if (document && document.body) {
2446                         DDM._timeoutCount += 1;
2447                     }
2448                 }
2449             }
2450         },
2451
2452         /**
2453          * Recursively searches the immediate parent and all child nodes for
2454          * the handle element in order to determine wheter or not it was
2455          * clicked.
2456          * @method handleWasClicked
2457          * @param node the html element to inspect
2458          * @static
2459          */
2460         handleWasClicked: function(node, id) {
2461             if (this.isHandle(id, node.id)) {
2462                 return true;
2463             } else {
2464                 // check to see if this is a text node child of the one we want
2465                 var p = node.parentNode;
2466
2467                 while (p) {
2468                     if (this.isHandle(id, p.id)) {
2469                         return true;
2470                     } else {
2471                         p = p.parentNode;
2472                     }
2473                 }
2474             }
2475
2476             return false;
2477         }
2478
2479     };
2480
2481 }();
2482
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2486
2487 }/*
2488  * Based on:
2489  * Ext JS Library 1.1.1
2490  * Copyright(c) 2006-2007, Ext JS, LLC.
2491  *
2492  * Originally Released Under LGPL - original licence link has changed is not relivant.
2493  *
2494  * Fork - LGPL
2495  * <script type="text/javascript">
2496  */
2497
2498 /**
2499  * @class Roo.dd.DD
2500  * A DragDrop implementation where the linked element follows the
2501  * mouse cursor during a drag.
2502  * @extends Roo.dd.DragDrop
2503  * @constructor
2504  * @param {String} id the id of the linked element
2505  * @param {String} sGroup the group of related DragDrop items
2506  * @param {object} config an object containing configurable attributes
2507  *                Valid properties for DD:
2508  *                    scroll
2509  */
2510 Roo.dd.DD = function(id, sGroup, config) {
2511     if (id) {
2512         this.init(id, sGroup, config);
2513     }
2514 };
2515
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2517
2518     /**
2519      * When set to true, the utility automatically tries to scroll the browser
2520      * window wehn a drag and drop element is dragged near the viewport boundary.
2521      * Defaults to true.
2522      * @property scroll
2523      * @type boolean
2524      */
2525     scroll: true,
2526
2527     /**
2528      * Sets the pointer offset to the distance between the linked element's top
2529      * left corner and the location the element was clicked
2530      * @method autoOffset
2531      * @param {int} iPageX the X coordinate of the click
2532      * @param {int} iPageY the Y coordinate of the click
2533      */
2534     autoOffset: function(iPageX, iPageY) {
2535         var x = iPageX - this.startPageX;
2536         var y = iPageY - this.startPageY;
2537         this.setDelta(x, y);
2538     },
2539
2540     /**
2541      * Sets the pointer offset.  You can call this directly to force the
2542      * offset to be in a particular location (e.g., pass in 0,0 to set it
2543      * to the center of the object)
2544      * @method setDelta
2545      * @param {int} iDeltaX the distance from the left
2546      * @param {int} iDeltaY the distance from the top
2547      */
2548     setDelta: function(iDeltaX, iDeltaY) {
2549         this.deltaX = iDeltaX;
2550         this.deltaY = iDeltaY;
2551     },
2552
2553     /**
2554      * Sets the drag element to the location of the mousedown or click event,
2555      * maintaining the cursor location relative to the location on the element
2556      * that was clicked.  Override this if you want to place the element in a
2557      * location other than where the cursor is.
2558      * @method setDragElPos
2559      * @param {int} iPageX the X coordinate of the mousedown or drag event
2560      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561      */
2562     setDragElPos: function(iPageX, iPageY) {
2563         // the first time we do this, we are going to check to make sure
2564         // the element has css positioning
2565
2566         var el = this.getDragEl();
2567         this.alignElWithMouse(el, iPageX, iPageY);
2568     },
2569
2570     /**
2571      * Sets the element to the location of the mousedown or click event,
2572      * maintaining the cursor location relative to the location on the element
2573      * that was clicked.  Override this if you want to place the element in a
2574      * location other than where the cursor is.
2575      * @method alignElWithMouse
2576      * @param {HTMLElement} el the element to move
2577      * @param {int} iPageX the X coordinate of the mousedown or drag event
2578      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2579      */
2580     alignElWithMouse: function(el, iPageX, iPageY) {
2581         var oCoord = this.getTargetCoord(iPageX, iPageY);
2582         var fly = el.dom ? el : Roo.fly(el);
2583         if (!this.deltaSetXY) {
2584             var aCoord = [oCoord.x, oCoord.y];
2585             fly.setXY(aCoord);
2586             var newLeft = fly.getLeft(true);
2587             var newTop  = fly.getTop(true);
2588             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2589         } else {
2590             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2591         }
2592
2593         this.cachePosition(oCoord.x, oCoord.y);
2594         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595         return oCoord;
2596     },
2597
2598     /**
2599      * Saves the most recent position so that we can reset the constraints and
2600      * tick marks on-demand.  We need to know this so that we can calculate the
2601      * number of pixels the element is offset from its original position.
2602      * @method cachePosition
2603      * @param iPageX the current x position (optional, this just makes it so we
2604      * don't have to look it up again)
2605      * @param iPageY the current y position (optional, this just makes it so we
2606      * don't have to look it up again)
2607      */
2608     cachePosition: function(iPageX, iPageY) {
2609         if (iPageX) {
2610             this.lastPageX = iPageX;
2611             this.lastPageY = iPageY;
2612         } else {
2613             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614             this.lastPageX = aCoord[0];
2615             this.lastPageY = aCoord[1];
2616         }
2617     },
2618
2619     /**
2620      * Auto-scroll the window if the dragged object has been moved beyond the
2621      * visible window boundary.
2622      * @method autoScroll
2623      * @param {int} x the drag element's x position
2624      * @param {int} y the drag element's y position
2625      * @param {int} h the height of the drag element
2626      * @param {int} w the width of the drag element
2627      * @private
2628      */
2629     autoScroll: function(x, y, h, w) {
2630
2631         if (this.scroll) {
2632             // The client height
2633             var clientH = Roo.lib.Dom.getViewWidth();
2634
2635             // The client width
2636             var clientW = Roo.lib.Dom.getViewHeight();
2637
2638             // The amt scrolled down
2639             var st = this.DDM.getScrollTop();
2640
2641             // The amt scrolled right
2642             var sl = this.DDM.getScrollLeft();
2643
2644             // Location of the bottom of the element
2645             var bot = h + y;
2646
2647             // Location of the right of the element
2648             var right = w + x;
2649
2650             // The distance from the cursor to the bottom of the visible area,
2651             // adjusted so that we don't scroll if the cursor is beyond the
2652             // element drag constraints
2653             var toBot = (clientH + st - y - this.deltaY);
2654
2655             // The distance from the cursor to the right of the visible area
2656             var toRight = (clientW + sl - x - this.deltaX);
2657
2658
2659             // How close to the edge the cursor must be before we scroll
2660             // var thresh = (document.all) ? 100 : 40;
2661             var thresh = 40;
2662
2663             // How many pixels to scroll per autoscroll op.  This helps to reduce
2664             // clunky scrolling. IE is more sensitive about this ... it needs this
2665             // value to be higher.
2666             var scrAmt = (document.all) ? 80 : 30;
2667
2668             // Scroll down if we are near the bottom of the visible page and the
2669             // obj extends below the crease
2670             if ( bot > clientH && toBot < thresh ) {
2671                 window.scrollTo(sl, st + scrAmt);
2672             }
2673
2674             // Scroll up if the window is scrolled down and the top of the object
2675             // goes above the top border
2676             if ( y < st && st > 0 && y - st < thresh ) {
2677                 window.scrollTo(sl, st - scrAmt);
2678             }
2679
2680             // Scroll right if the obj is beyond the right border and the cursor is
2681             // near the border.
2682             if ( right > clientW && toRight < thresh ) {
2683                 window.scrollTo(sl + scrAmt, st);
2684             }
2685
2686             // Scroll left if the window has been scrolled to the right and the obj
2687             // extends past the left border
2688             if ( x < sl && sl > 0 && x - sl < thresh ) {
2689                 window.scrollTo(sl - scrAmt, st);
2690             }
2691         }
2692     },
2693
2694     /**
2695      * Finds the location the element should be placed if we want to move
2696      * it to where the mouse location less the click offset would place us.
2697      * @method getTargetCoord
2698      * @param {int} iPageX the X coordinate of the click
2699      * @param {int} iPageY the Y coordinate of the click
2700      * @return an object that contains the coordinates (Object.x and Object.y)
2701      * @private
2702      */
2703     getTargetCoord: function(iPageX, iPageY) {
2704
2705
2706         var x = iPageX - this.deltaX;
2707         var y = iPageY - this.deltaY;
2708
2709         if (this.constrainX) {
2710             if (x < this.minX) { x = this.minX; }
2711             if (x > this.maxX) { x = this.maxX; }
2712         }
2713
2714         if (this.constrainY) {
2715             if (y < this.minY) { y = this.minY; }
2716             if (y > this.maxY) { y = this.maxY; }
2717         }
2718
2719         x = this.getTick(x, this.xTicks);
2720         y = this.getTick(y, this.yTicks);
2721
2722
2723         return {x:x, y:y};
2724     },
2725
2726     /*
2727      * Sets up config options specific to this class. Overrides
2728      * Roo.dd.DragDrop, but all versions of this method through the
2729      * inheritance chain are called
2730      */
2731     applyConfig: function() {
2732         Roo.dd.DD.superclass.applyConfig.call(this);
2733         this.scroll = (this.config.scroll !== false);
2734     },
2735
2736     /*
2737      * Event that fires prior to the onMouseDown event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4MouseDown: function(e) {
2741         // this.resetConstraints();
2742         this.autoOffset(e.getPageX(),
2743                             e.getPageY());
2744     },
2745
2746     /*
2747      * Event that fires prior to the onDrag event.  Overrides
2748      * Roo.dd.DragDrop.
2749      */
2750     b4Drag: function(e) {
2751         this.setDragElPos(e.getPageX(),
2752                             e.getPageY());
2753     },
2754
2755     toString: function() {
2756         return ("DD " + this.id);
2757     }
2758
2759     //////////////////////////////////////////////////////////////////////////
2760     // Debugging ygDragDrop events that can be overridden
2761     //////////////////////////////////////////////////////////////////////////
2762     /*
2763     startDrag: function(x, y) {
2764     },
2765
2766     onDrag: function(e) {
2767     },
2768
2769     onDragEnter: function(e, id) {
2770     },
2771
2772     onDragOver: function(e, id) {
2773     },
2774
2775     onDragOut: function(e, id) {
2776     },
2777
2778     onDragDrop: function(e, id) {
2779     },
2780
2781     endDrag: function(e) {
2782     }
2783
2784     */
2785
2786 });/*
2787  * Based on:
2788  * Ext JS Library 1.1.1
2789  * Copyright(c) 2006-2007, Ext JS, LLC.
2790  *
2791  * Originally Released Under LGPL - original licence link has changed is not relivant.
2792  *
2793  * Fork - LGPL
2794  * <script type="text/javascript">
2795  */
2796
2797 /**
2798  * @class Roo.dd.DDProxy
2799  * A DragDrop implementation that inserts an empty, bordered div into
2800  * the document that follows the cursor during drag operations.  At the time of
2801  * the click, the frame div is resized to the dimensions of the linked html
2802  * element, and moved to the exact location of the linked element.
2803  *
2804  * References to the "frame" element refer to the single proxy element that
2805  * was created to be dragged in place of all DDProxy elements on the
2806  * page.
2807  *
2808  * @extends Roo.dd.DD
2809  * @constructor
2810  * @param {String} id the id of the linked html element
2811  * @param {String} sGroup the group of related DragDrop objects
2812  * @param {object} config an object containing configurable attributes
2813  *                Valid properties for DDProxy in addition to those in DragDrop:
2814  *                   resizeFrame, centerFrame, dragElId
2815  */
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2817     if (id) {
2818         this.init(id, sGroup, config);
2819         this.initFrame();
2820     }
2821 };
2822
2823 /**
2824  * The default drag frame div id
2825  * @property Roo.dd.DDProxy.dragElId
2826  * @type String
2827  * @static
2828  */
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2830
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2832
2833     /**
2834      * By default we resize the drag frame to be the same size as the element
2835      * we want to drag (this is to get the frame effect).  We can turn it off
2836      * if we want a different behavior.
2837      * @property resizeFrame
2838      * @type boolean
2839      */
2840     resizeFrame: true,
2841
2842     /**
2843      * By default the frame is positioned exactly where the drag element is, so
2844      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2845      * you do not have constraints on the obj is to have the drag frame centered
2846      * around the cursor.  Set centerFrame to true for this effect.
2847      * @property centerFrame
2848      * @type boolean
2849      */
2850     centerFrame: false,
2851
2852     /**
2853      * Creates the proxy element if it does not yet exist
2854      * @method createFrame
2855      */
2856     createFrame: function() {
2857         var self = this;
2858         var body = document.body;
2859
2860         if (!body || !body.firstChild) {
2861             setTimeout( function() { self.createFrame(); }, 50 );
2862             return;
2863         }
2864
2865         var div = this.getDragEl();
2866
2867         if (!div) {
2868             div    = document.createElement("div");
2869             div.id = this.dragElId;
2870             var s  = div.style;
2871
2872             s.position   = "absolute";
2873             s.visibility = "hidden";
2874             s.cursor     = "move";
2875             s.border     = "2px solid #aaa";
2876             s.zIndex     = 999;
2877
2878             // appendChild can blow up IE if invoked prior to the window load event
2879             // while rendering a table.  It is possible there are other scenarios
2880             // that would cause this to happen as well.
2881             body.insertBefore(div, body.firstChild);
2882         }
2883     },
2884
2885     /**
2886      * Initialization for the drag frame element.  Must be called in the
2887      * constructor of all subclasses
2888      * @method initFrame
2889      */
2890     initFrame: function() {
2891         this.createFrame();
2892     },
2893
2894     applyConfig: function() {
2895         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2896
2897         this.resizeFrame = (this.config.resizeFrame !== false);
2898         this.centerFrame = (this.config.centerFrame);
2899         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2900     },
2901
2902     /**
2903      * Resizes the drag frame to the dimensions of the clicked object, positions
2904      * it over the object, and finally displays it
2905      * @method showFrame
2906      * @param {int} iPageX X click position
2907      * @param {int} iPageY Y click position
2908      * @private
2909      */
2910     showFrame: function(iPageX, iPageY) {
2911         var el = this.getEl();
2912         var dragEl = this.getDragEl();
2913         var s = dragEl.style;
2914
2915         this._resizeProxy();
2916
2917         if (this.centerFrame) {
2918             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2919                            Math.round(parseInt(s.height, 10)/2) );
2920         }
2921
2922         this.setDragElPos(iPageX, iPageY);
2923
2924         Roo.fly(dragEl).show();
2925     },
2926
2927     /**
2928      * The proxy is automatically resized to the dimensions of the linked
2929      * element when a drag is initiated, unless resizeFrame is set to false
2930      * @method _resizeProxy
2931      * @private
2932      */
2933     _resizeProxy: function() {
2934         if (this.resizeFrame) {
2935             var el = this.getEl();
2936             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2937         }
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4MouseDown: function(e) {
2942         var x = e.getPageX();
2943         var y = e.getPageY();
2944         this.autoOffset(x, y);
2945         this.setDragElPos(x, y);
2946     },
2947
2948     // overrides Roo.dd.DragDrop
2949     b4StartDrag: function(x, y) {
2950         // show the drag frame
2951         this.showFrame(x, y);
2952     },
2953
2954     // overrides Roo.dd.DragDrop
2955     b4EndDrag: function(e) {
2956         Roo.fly(this.getDragEl()).hide();
2957     },
2958
2959     // overrides Roo.dd.DragDrop
2960     // By default we try to move the element to the last location of the frame.
2961     // This is so that the default behavior mirrors that of Roo.dd.DD.
2962     endDrag: function(e) {
2963
2964         var lel = this.getEl();
2965         var del = this.getDragEl();
2966
2967         // Show the drag frame briefly so we can get its position
2968         del.style.visibility = "";
2969
2970         this.beforeMove();
2971         // Hide the linked element before the move to get around a Safari
2972         // rendering bug.
2973         lel.style.visibility = "hidden";
2974         Roo.dd.DDM.moveToEl(lel, del);
2975         del.style.visibility = "hidden";
2976         lel.style.visibility = "";
2977
2978         this.afterDrag();
2979     },
2980
2981     beforeMove : function(){
2982
2983     },
2984
2985     afterDrag : function(){
2986
2987     },
2988
2989     toString: function() {
2990         return ("DDProxy " + this.id);
2991     }
2992
2993 });
2994 /*
2995  * Based on:
2996  * Ext JS Library 1.1.1
2997  * Copyright(c) 2006-2007, Ext JS, LLC.
2998  *
2999  * Originally Released Under LGPL - original licence link has changed is not relivant.
3000  *
3001  * Fork - LGPL
3002  * <script type="text/javascript">
3003  */
3004
3005  /**
3006  * @class Roo.dd.DDTarget
3007  * A DragDrop implementation that does not move, but can be a drop
3008  * target.  You would get the same result by simply omitting implementation
3009  * for the event callbacks, but this way we reduce the processing cost of the
3010  * event listener and the callbacks.
3011  * @extends Roo.dd.DragDrop
3012  * @constructor
3013  * @param {String} id the id of the element that is a drop target
3014  * @param {String} sGroup the group of related DragDrop objects
3015  * @param {object} config an object containing configurable attributes
3016  *                 Valid properties for DDTarget in addition to those in
3017  *                 DragDrop:
3018  *                    none
3019  */
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3021     if (id) {
3022         this.initTarget(id, sGroup, config);
3023     }
3024     if (config.listeners || config.events) { 
3025        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3026             listeners : config.listeners || {}, 
3027             events : config.events || {} 
3028         });    
3029     }
3030 };
3031
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034     toString: function() {
3035         return ("DDTarget " + this.id);
3036     }
3037 });
3038 /*
3039  * Based on:
3040  * Ext JS Library 1.1.1
3041  * Copyright(c) 2006-2007, Ext JS, LLC.
3042  *
3043  * Originally Released Under LGPL - original licence link has changed is not relivant.
3044  *
3045  * Fork - LGPL
3046  * <script type="text/javascript">
3047  */
3048  
3049
3050 /**
3051  * @class Roo.dd.ScrollManager
3052  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3054  * @singleton
3055  */
3056 Roo.dd.ScrollManager = function(){
3057     var ddm = Roo.dd.DragDropMgr;
3058     var els = {};
3059     var dragEl = null;
3060     var proc = {};
3061     
3062     
3063     
3064     var onStop = function(e){
3065         dragEl = null;
3066         clearProc();
3067     };
3068     
3069     var triggerRefresh = function(){
3070         if(ddm.dragCurrent){
3071              ddm.refreshCache(ddm.dragCurrent.groups);
3072         }
3073     };
3074     
3075     var doScroll = function(){
3076         if(ddm.dragCurrent){
3077             var dds = Roo.dd.ScrollManager;
3078             if(!dds.animate){
3079                 if(proc.el.scroll(proc.dir, dds.increment)){
3080                     triggerRefresh();
3081                 }
3082             }else{
3083                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084             }
3085         }
3086     };
3087     
3088     var clearProc = function(){
3089         if(proc.id){
3090             clearInterval(proc.id);
3091         }
3092         proc.id = 0;
3093         proc.el = null;
3094         proc.dir = "";
3095     };
3096     
3097     var startProc = function(el, dir){
3098          Roo.log('scroll startproc');
3099         clearProc();
3100         proc.el = el;
3101         proc.dir = dir;
3102         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3103     };
3104     
3105     var onFire = function(e, isDrop){
3106        
3107         if(isDrop || !ddm.dragCurrent){ return; }
3108         var dds = Roo.dd.ScrollManager;
3109         if(!dragEl || dragEl != ddm.dragCurrent){
3110             dragEl = ddm.dragCurrent;
3111             // refresh regions on drag start
3112             dds.refreshCache();
3113         }
3114         
3115         var xy = Roo.lib.Event.getXY(e);
3116         var pt = new Roo.lib.Point(xy[0], xy[1]);
3117         for(var id in els){
3118             var el = els[id], r = el._region;
3119             if(r && r.contains(pt) && el.isScrollable()){
3120                 if(r.bottom - pt.y <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "down");
3123                     }
3124                     return;
3125                 }else if(r.right - pt.x <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "left");
3128                     }
3129                     return;
3130                 }else if(pt.y - r.top <= dds.thresh){
3131                     if(proc.el != el){
3132                         startProc(el, "up");
3133                     }
3134                     return;
3135                 }else if(pt.x - r.left <= dds.thresh){
3136                     if(proc.el != el){
3137                         startProc(el, "right");
3138                     }
3139                     return;
3140                 }
3141             }
3142         }
3143         clearProc();
3144     };
3145     
3146     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3148     
3149     return {
3150         /**
3151          * Registers new overflow element(s) to auto scroll
3152          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3153          */
3154         register : function(el){
3155             if(el instanceof Array){
3156                 for(var i = 0, len = el.length; i < len; i++) {
3157                         this.register(el[i]);
3158                 }
3159             }else{
3160                 el = Roo.get(el);
3161                 els[el.id] = el;
3162             }
3163             Roo.dd.ScrollManager.els = els;
3164         },
3165         
3166         /**
3167          * Unregisters overflow element(s) so they are no longer scrolled
3168          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3169          */
3170         unregister : function(el){
3171             if(el instanceof Array){
3172                 for(var i = 0, len = el.length; i < len; i++) {
3173                         this.unregister(el[i]);
3174                 }
3175             }else{
3176                 el = Roo.get(el);
3177                 delete els[el.id];
3178             }
3179         },
3180         
3181         /**
3182          * The number of pixels from the edge of a container the pointer needs to be to 
3183          * trigger scrolling (defaults to 25)
3184          * @type Number
3185          */
3186         thresh : 25,
3187         
3188         /**
3189          * The number of pixels to scroll in each scroll increment (defaults to 50)
3190          * @type Number
3191          */
3192         increment : 100,
3193         
3194         /**
3195          * The frequency of scrolls in milliseconds (defaults to 500)
3196          * @type Number
3197          */
3198         frequency : 500,
3199         
3200         /**
3201          * True to animate the scroll (defaults to true)
3202          * @type Boolean
3203          */
3204         animate: true,
3205         
3206         /**
3207          * The animation duration in seconds - 
3208          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3209          * @type Number
3210          */
3211         animDuration: .4,
3212         
3213         /**
3214          * Manually trigger a cache refresh.
3215          */
3216         refreshCache : function(){
3217             for(var id in els){
3218                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219                     els[id]._region = els[id].getRegion();
3220                 }
3221             }
3222         }
3223     };
3224 }();/*
3225  * Based on:
3226  * Ext JS Library 1.1.1
3227  * Copyright(c) 2006-2007, Ext JS, LLC.
3228  *
3229  * Originally Released Under LGPL - original licence link has changed is not relivant.
3230  *
3231  * Fork - LGPL
3232  * <script type="text/javascript">
3233  */
3234  
3235
3236 /**
3237  * @class Roo.dd.Registry
3238  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3239  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3240  * @singleton
3241  */
3242 Roo.dd.Registry = function(){
3243     var elements = {}; 
3244     var handles = {}; 
3245     var autoIdSeed = 0;
3246
3247     var getId = function(el, autogen){
3248         if(typeof el == "string"){
3249             return el;
3250         }
3251         var id = el.id;
3252         if(!id && autogen !== false){
3253             id = "roodd-" + (++autoIdSeed);
3254             el.id = id;
3255         }
3256         return id;
3257     };
3258     
3259     return {
3260     /**
3261      * Register a drag drop element
3262      * @param {String|HTMLElement} element The id or DOM node to register
3263      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3265      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266      * populated in the data object (if applicable):
3267      * <pre>
3268 Value      Description<br />
3269 ---------  ------------------------------------------<br />
3270 handles    Array of DOM nodes that trigger dragging<br />
3271            for the element being registered<br />
3272 isHandle   True if the element passed in triggers<br />
3273            dragging itself, else false
3274 </pre>
3275      */
3276         register : function(el, data){
3277             data = data || {};
3278             if(typeof el == "string"){
3279                 el = document.getElementById(el);
3280             }
3281             data.ddel = el;
3282             elements[getId(el)] = data;
3283             if(data.isHandle !== false){
3284                 handles[data.ddel.id] = data;
3285             }
3286             if(data.handles){
3287                 var hs = data.handles;
3288                 for(var i = 0, len = hs.length; i < len; i++){
3289                         handles[getId(hs[i])] = data;
3290                 }
3291             }
3292         },
3293
3294     /**
3295      * Unregister a drag drop element
3296      * @param {String|HTMLElement}  element The id or DOM node to unregister
3297      */
3298         unregister : function(el){
3299             var id = getId(el, false);
3300             var data = elements[id];
3301             if(data){
3302                 delete elements[id];
3303                 if(data.handles){
3304                     var hs = data.handles;
3305                     for(var i = 0, len = hs.length; i < len; i++){
3306                         delete handles[getId(hs[i], false)];
3307                     }
3308                 }
3309             }
3310         },
3311
3312     /**
3313      * Returns the handle registered for a DOM Node by id
3314      * @param {String|HTMLElement} id The DOM node or id to look up
3315      * @return {Object} handle The custom handle data
3316      */
3317         getHandle : function(id){
3318             if(typeof id != "string"){ // must be element?
3319                 id = id.id;
3320             }
3321             return handles[id];
3322         },
3323
3324     /**
3325      * Returns the handle that is registered for the DOM node that is the target of the event
3326      * @param {Event} e The event
3327      * @return {Object} handle The custom handle data
3328      */
3329         getHandleFromEvent : function(e){
3330             var t = Roo.lib.Event.getTarget(e);
3331             return t ? handles[t.id] : null;
3332         },
3333
3334     /**
3335      * Returns a custom data object that is registered for a DOM node by id
3336      * @param {String|HTMLElement} id The DOM node or id to look up
3337      * @return {Object} data The custom data
3338      */
3339         getTarget : function(id){
3340             if(typeof id != "string"){ // must be element?
3341                 id = id.id;
3342             }
3343             return elements[id];
3344         },
3345
3346     /**
3347      * Returns a custom data object that is registered for the DOM node that is the target of the event
3348      * @param {Event} e The event
3349      * @return {Object} data The custom data
3350      */
3351         getTargetFromEvent : function(e){
3352             var t = Roo.lib.Event.getTarget(e);
3353             return t ? elements[t.id] || handles[t.id] : null;
3354         }
3355     };
3356 }();/*
3357  * Based on:
3358  * Ext JS Library 1.1.1
3359  * Copyright(c) 2006-2007, Ext JS, LLC.
3360  *
3361  * Originally Released Under LGPL - original licence link has changed is not relivant.
3362  *
3363  * Fork - LGPL
3364  * <script type="text/javascript">
3365  */
3366  
3367
3368 /**
3369  * @class Roo.dd.StatusProxy
3370  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3371  * default drag proxy used by all Roo.dd components.
3372  * @constructor
3373  * @param {Object} config
3374  */
3375 Roo.dd.StatusProxy = function(config){
3376     Roo.apply(this, config);
3377     this.id = this.id || Roo.id();
3378     this.el = new Roo.Layer({
3379         dh: {
3380             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381                 {tag: "div", cls: "x-dd-drop-icon"},
3382                 {tag: "div", cls: "x-dd-drag-ghost"}
3383             ]
3384         }, 
3385         shadow: !config || config.shadow !== false
3386     });
3387     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388     this.dropStatus = this.dropNotAllowed;
3389 };
3390
3391 Roo.dd.StatusProxy.prototype = {
3392     /**
3393      * @cfg {String} dropAllowed
3394      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3395      */
3396     dropAllowed : "x-dd-drop-ok",
3397     /**
3398      * @cfg {String} dropNotAllowed
3399      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3400      */
3401     dropNotAllowed : "x-dd-drop-nodrop",
3402
3403     /**
3404      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405      * over the current target element.
3406      * @param {String} cssClass The css class for the new drop status indicator image
3407      */
3408     setStatus : function(cssClass){
3409         cssClass = cssClass || this.dropNotAllowed;
3410         if(this.dropStatus != cssClass){
3411             this.el.replaceClass(this.dropStatus, cssClass);
3412             this.dropStatus = cssClass;
3413         }
3414     },
3415
3416     /**
3417      * Resets the status indicator to the default dropNotAllowed value
3418      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3419      */
3420     reset : function(clearGhost){
3421         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422         this.dropStatus = this.dropNotAllowed;
3423         if(clearGhost){
3424             this.ghost.update("");
3425         }
3426     },
3427
3428     /**
3429      * Updates the contents of the ghost element
3430      * @param {String} html The html that will replace the current innerHTML of the ghost element
3431      */
3432     update : function(html){
3433         if(typeof html == "string"){
3434             this.ghost.update(html);
3435         }else{
3436             this.ghost.update("");
3437             html.style.margin = "0";
3438             this.ghost.dom.appendChild(html);
3439         }
3440         // ensure float = none set?? cant remember why though.
3441         var el = this.ghost.dom.firstChild;
3442                 if(el){
3443                         Roo.fly(el).setStyle('float', 'none');
3444                 }
3445     },
3446     
3447     /**
3448      * Returns the underlying proxy {@link Roo.Layer}
3449      * @return {Roo.Layer} el
3450     */
3451     getEl : function(){
3452         return this.el;
3453     },
3454
3455     /**
3456      * Returns the ghost element
3457      * @return {Roo.Element} el
3458      */
3459     getGhost : function(){
3460         return this.ghost;
3461     },
3462
3463     /**
3464      * Hides the proxy
3465      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3466      */
3467     hide : function(clear){
3468         this.el.hide();
3469         if(clear){
3470             this.reset(true);
3471         }
3472     },
3473
3474     /**
3475      * Stops the repair animation if it's currently running
3476      */
3477     stop : function(){
3478         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3479             this.anim.stop();
3480         }
3481     },
3482
3483     /**
3484      * Displays this proxy
3485      */
3486     show : function(){
3487         this.el.show();
3488     },
3489
3490     /**
3491      * Force the Layer to sync its shadow and shim positions to the element
3492      */
3493     sync : function(){
3494         this.el.sync();
3495     },
3496
3497     /**
3498      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3499      * invalid drop operation by the item being dragged.
3500      * @param {Array} xy The XY position of the element ([x, y])
3501      * @param {Function} callback The function to call after the repair is complete
3502      * @param {Object} scope The scope in which to execute the callback
3503      */
3504     repair : function(xy, callback, scope){
3505         this.callback = callback;
3506         this.scope = scope;
3507         if(xy && this.animRepair !== false){
3508             this.el.addClass("x-dd-drag-repair");
3509             this.el.hideUnders(true);
3510             this.anim = this.el.shift({
3511                 duration: this.repairDuration || .5,
3512                 easing: 'easeOut',
3513                 xy: xy,
3514                 stopFx: true,
3515                 callback: this.afterRepair,
3516                 scope: this
3517             });
3518         }else{
3519             this.afterRepair();
3520         }
3521     },
3522
3523     // private
3524     afterRepair : function(){
3525         this.hide(true);
3526         if(typeof this.callback == "function"){
3527             this.callback.call(this.scope || this);
3528         }
3529         this.callback = null;
3530         this.scope = null;
3531     }
3532 };/*
3533  * Based on:
3534  * Ext JS Library 1.1.1
3535  * Copyright(c) 2006-2007, Ext JS, LLC.
3536  *
3537  * Originally Released Under LGPL - original licence link has changed is not relivant.
3538  *
3539  * Fork - LGPL
3540  * <script type="text/javascript">
3541  */
3542
3543 /**
3544  * @class Roo.dd.DragSource
3545  * @extends Roo.dd.DDProxy
3546  * A simple class that provides the basic implementation needed to make any element draggable.
3547  * @constructor
3548  * @param {String/HTMLElement/Element} el The container element
3549  * @param {Object} config
3550  */
3551 Roo.dd.DragSource = function(el, config){
3552     this.el = Roo.get(el);
3553     this.dragData = {};
3554     
3555     Roo.apply(this, config);
3556     
3557     if(!this.proxy){
3558         this.proxy = new Roo.dd.StatusProxy();
3559     }
3560
3561     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3563     
3564     this.dragging = false;
3565 };
3566
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3568     /**
3569      * @cfg {String} dropAllowed
3570      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3571      */
3572     dropAllowed : "x-dd-drop-ok",
3573     /**
3574      * @cfg {String} dropNotAllowed
3575      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3576      */
3577     dropNotAllowed : "x-dd-drop-nodrop",
3578
3579     /**
3580      * Returns the data object associated with this drag source
3581      * @return {Object} data An object containing arbitrary data
3582      */
3583     getDragData : function(e){
3584         return this.dragData;
3585     },
3586
3587     // private
3588     onDragEnter : function(e, id){
3589         var target = Roo.dd.DragDropMgr.getDDById(id);
3590         this.cachedTarget = target;
3591         if(this.beforeDragEnter(target, e, id) !== false){
3592             if(target.isNotifyTarget){
3593                 var status = target.notifyEnter(this, e, this.dragData);
3594                 this.proxy.setStatus(status);
3595             }else{
3596                 this.proxy.setStatus(this.dropAllowed);
3597             }
3598             
3599             if(this.afterDragEnter){
3600                 /**
3601                  * An empty function by default, but provided so that you can perform a custom action
3602                  * when the dragged item enters the drop target by providing an implementation.
3603                  * @param {Roo.dd.DragDrop} target The drop target
3604                  * @param {Event} e The event object
3605                  * @param {String} id The id of the dragged element
3606                  * @method afterDragEnter
3607                  */
3608                 this.afterDragEnter(target, e, id);
3609             }
3610         }
3611     },
3612
3613     /**
3614      * An empty function by default, but provided so that you can perform a custom action
3615      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616      * @param {Roo.dd.DragDrop} target The drop target
3617      * @param {Event} e The event object
3618      * @param {String} id The id of the dragged element
3619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3620      */
3621     beforeDragEnter : function(target, e, id){
3622         return true;
3623     },
3624
3625     // private
3626     alignElWithMouse: function() {
3627         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628         this.proxy.sync();
3629     },
3630
3631     // private
3632     onDragOver : function(e, id){
3633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634         if(this.beforeDragOver(target, e, id) !== false){
3635             if(target.isNotifyTarget){
3636                 var status = target.notifyOver(this, e, this.dragData);
3637                 this.proxy.setStatus(status);
3638             }
3639
3640             if(this.afterDragOver){
3641                 /**
3642                  * An empty function by default, but provided so that you can perform a custom action
3643                  * while the dragged item is over the drop target by providing an implementation.
3644                  * @param {Roo.dd.DragDrop} target The drop target
3645                  * @param {Event} e The event object
3646                  * @param {String} id The id of the dragged element
3647                  * @method afterDragOver
3648                  */
3649                 this.afterDragOver(target, e, id);
3650             }
3651         }
3652     },
3653
3654     /**
3655      * An empty function by default, but provided so that you can perform a custom action
3656      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657      * @param {Roo.dd.DragDrop} target The drop target
3658      * @param {Event} e The event object
3659      * @param {String} id The id of the dragged element
3660      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3661      */
3662     beforeDragOver : function(target, e, id){
3663         return true;
3664     },
3665
3666     // private
3667     onDragOut : function(e, id){
3668         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669         if(this.beforeDragOut(target, e, id) !== false){
3670             if(target.isNotifyTarget){
3671                 target.notifyOut(this, e, this.dragData);
3672             }
3673             this.proxy.reset();
3674             if(this.afterDragOut){
3675                 /**
3676                  * An empty function by default, but provided so that you can perform a custom action
3677                  * after the dragged item is dragged out of the target without dropping.
3678                  * @param {Roo.dd.DragDrop} target The drop target
3679                  * @param {Event} e The event object
3680                  * @param {String} id The id of the dragged element
3681                  * @method afterDragOut
3682                  */
3683                 this.afterDragOut(target, e, id);
3684             }
3685         }
3686         this.cachedTarget = null;
3687     },
3688
3689     /**
3690      * An empty function by default, but provided so that you can perform a custom action before the dragged
3691      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692      * @param {Roo.dd.DragDrop} target The drop target
3693      * @param {Event} e The event object
3694      * @param {String} id The id of the dragged element
3695      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3696      */
3697     beforeDragOut : function(target, e, id){
3698         return true;
3699     },
3700     
3701     // private
3702     onDragDrop : function(e, id){
3703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704         if(this.beforeDragDrop(target, e, id) !== false){
3705             if(target.isNotifyTarget){
3706                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707                     this.onValidDrop(target, e, id);
3708                 }else{
3709                     this.onInvalidDrop(target, e, id);
3710                 }
3711             }else{
3712                 this.onValidDrop(target, e, id);
3713             }
3714             
3715             if(this.afterDragDrop){
3716                 /**
3717                  * An empty function by default, but provided so that you can perform a custom action
3718                  * after a valid drag drop has occurred by providing an implementation.
3719                  * @param {Roo.dd.DragDrop} target The drop target
3720                  * @param {Event} e The event object
3721                  * @param {String} id The id of the dropped element
3722                  * @method afterDragDrop
3723                  */
3724                 this.afterDragDrop(target, e, id);
3725             }
3726         }
3727         delete this.cachedTarget;
3728     },
3729
3730     /**
3731      * An empty function by default, but provided so that you can perform a custom action before the dragged
3732      * item is dropped onto the target and optionally cancel the onDragDrop.
3733      * @param {Roo.dd.DragDrop} target The drop target
3734      * @param {Event} e The event object
3735      * @param {String} id The id of the dragged element
3736      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3737      */
3738     beforeDragDrop : function(target, e, id){
3739         return true;
3740     },
3741
3742     // private
3743     onValidDrop : function(target, e, id){
3744         this.hideProxy();
3745         if(this.afterValidDrop){
3746             /**
3747              * An empty function by default, but provided so that you can perform a custom action
3748              * after a valid drop has occurred by providing an implementation.
3749              * @param {Object} target The target DD 
3750              * @param {Event} e The event object
3751              * @param {String} id The id of the dropped element
3752              * @method afterInvalidDrop
3753              */
3754             this.afterValidDrop(target, e, id);
3755         }
3756     },
3757
3758     // private
3759     getRepairXY : function(e, data){
3760         return this.el.getXY();  
3761     },
3762
3763     // private
3764     onInvalidDrop : function(target, e, id){
3765         this.beforeInvalidDrop(target, e, id);
3766         if(this.cachedTarget){
3767             if(this.cachedTarget.isNotifyTarget){
3768                 this.cachedTarget.notifyOut(this, e, this.dragData);
3769             }
3770             this.cacheTarget = null;
3771         }
3772         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3773
3774         if(this.afterInvalidDrop){
3775             /**
3776              * An empty function by default, but provided so that you can perform a custom action
3777              * after an invalid drop has occurred by providing an implementation.
3778              * @param {Event} e The event object
3779              * @param {String} id The id of the dropped element
3780              * @method afterInvalidDrop
3781              */
3782             this.afterInvalidDrop(e, id);
3783         }
3784     },
3785
3786     // private
3787     afterRepair : function(){
3788         if(Roo.enableFx){
3789             this.el.highlight(this.hlColor || "c3daf9");
3790         }
3791         this.dragging = false;
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action after an invalid
3796      * drop has occurred.
3797      * @param {Roo.dd.DragDrop} target The drop target
3798      * @param {Event} e The event object
3799      * @param {String} id The id of the dragged element
3800      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3801      */
3802     beforeInvalidDrop : function(target, e, id){
3803         return true;
3804     },
3805
3806     // private
3807     handleMouseDown : function(e){
3808         if(this.dragging) {
3809             return;
3810         }
3811         var data = this.getDragData(e);
3812         if(data && this.onBeforeDrag(data, e) !== false){
3813             this.dragData = data;
3814             this.proxy.stop();
3815             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816         } 
3817     },
3818
3819     /**
3820      * An empty function by default, but provided so that you can perform a custom action before the initial
3821      * drag event begins and optionally cancel it.
3822      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823      * @param {Event} e The event object
3824      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3825      */
3826     onBeforeDrag : function(data, e){
3827         return true;
3828     },
3829
3830     /**
3831      * An empty function by default, but provided so that you can perform a custom action once the initial
3832      * drag event has begun.  The drag cannot be canceled from this function.
3833      * @param {Number} x The x position of the click on the dragged object
3834      * @param {Number} y The y position of the click on the dragged object
3835      */
3836     onStartDrag : Roo.emptyFn,
3837
3838     // private - YUI override
3839     startDrag : function(x, y){
3840         this.proxy.reset();
3841         this.dragging = true;
3842         this.proxy.update("");
3843         this.onInitDrag(x, y);
3844         this.proxy.show();
3845     },
3846
3847     // private
3848     onInitDrag : function(x, y){
3849         var clone = this.el.dom.cloneNode(true);
3850         clone.id = Roo.id(); // prevent duplicate ids
3851         this.proxy.update(clone);
3852         this.onStartDrag(x, y);
3853         return true;
3854     },
3855
3856     /**
3857      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3859      */
3860     getProxy : function(){
3861         return this.proxy;  
3862     },
3863
3864     /**
3865      * Hides the drag source's {@link Roo.dd.StatusProxy}
3866      */
3867     hideProxy : function(){
3868         this.proxy.hide();  
3869         this.proxy.reset(true);
3870         this.dragging = false;
3871     },
3872
3873     // private
3874     triggerCacheRefresh : function(){
3875         Roo.dd.DDM.refreshCache(this.groups);
3876     },
3877
3878     // private - override to prevent hiding
3879     b4EndDrag: function(e) {
3880     },
3881
3882     // private - override to prevent moving
3883     endDrag : function(e){
3884         this.onEndDrag(this.dragData, e);
3885     },
3886
3887     // private
3888     onEndDrag : function(data, e){
3889     },
3890     
3891     // private - pin to cursor
3892     autoOffset : function(x, y) {
3893         this.setDelta(-12, -20);
3894     }    
3895 });/*
3896  * Based on:
3897  * Ext JS Library 1.1.1
3898  * Copyright(c) 2006-2007, Ext JS, LLC.
3899  *
3900  * Originally Released Under LGPL - original licence link has changed is not relivant.
3901  *
3902  * Fork - LGPL
3903  * <script type="text/javascript">
3904  */
3905
3906
3907 /**
3908  * @class Roo.dd.DropTarget
3909  * @extends Roo.dd.DDTarget
3910  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3912  * @constructor
3913  * @param {String/HTMLElement/Element} el The container element
3914  * @param {Object} config
3915  */
3916 Roo.dd.DropTarget = function(el, config){
3917     this.el = Roo.get(el);
3918     
3919     var listeners = false; ;
3920     if (config && config.listeners) {
3921         listeners= config.listeners;
3922         delete config.listeners;
3923     }
3924     Roo.apply(this, config);
3925     
3926     if(this.containerScroll){
3927         Roo.dd.ScrollManager.register(this.el);
3928     }
3929     this.addEvents( {
3930          /**
3931          * @scope Roo.dd.DropTarget
3932          */
3933          
3934          /**
3935          * @event enter
3936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3939          * 
3940          * IMPORTANT : it should set this.overClass and this.dropAllowed
3941          * 
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946         "enter" : true,
3947         
3948          /**
3949          * @event over
3950          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951          * This method will be called on every mouse movement while the drag source is over the drop target.
3952          * This default implementation simply returns the dropAllowed config value.
3953          * 
3954          * IMPORTANT : it should set this.dropAllowed
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959          
3960          */
3961         "over" : true,
3962         /**
3963          * @event out
3964          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3966          * overClass (if any) from the drop element.
3967          * 
3968          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969          * @param {Event} e The event
3970          * @param {Object} data An object containing arbitrary data supplied by the drag source
3971          */
3972          "out" : true,
3973          
3974         /**
3975          * @event drop
3976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3978          * implementation that does something to process the drop event and returns true so that the drag source's
3979          * repair action does not run.
3980          * 
3981          * IMPORTANT : it should set this.success
3982          * 
3983          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984          * @param {Event} e The event
3985          * @param {Object} data An object containing arbitrary data supplied by the drag source
3986         */
3987          "drop" : true
3988     });
3989             
3990      
3991     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3992         this.el.dom, 
3993         this.ddGroup || this.group,
3994         {
3995             isTarget: true,
3996             listeners : listeners || {} 
3997            
3998         
3999         }
4000     );
4001
4002 };
4003
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4005     /**
4006      * @cfg {String} overClass
4007      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4008      */
4009      /**
4010      * @cfg {String} ddGroup
4011      * The drag drop group to handle drop events for
4012      */
4013      
4014     /**
4015      * @cfg {String} dropAllowed
4016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4017      */
4018     dropAllowed : "x-dd-drop-ok",
4019     /**
4020      * @cfg {String} dropNotAllowed
4021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4022      */
4023     dropNotAllowed : "x-dd-drop-nodrop",
4024     /**
4025      * @cfg {boolean} success
4026      * set this after drop listener.. 
4027      */
4028     success : false,
4029     /**
4030      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031      * if the drop point is valid for over/enter..
4032      */
4033     valid : false,
4034     // private
4035     isTarget : true,
4036
4037     // private
4038     isNotifyTarget : true,
4039     
4040     /**
4041      * @hide
4042      */
4043     notifyEnter : function(dd, e, data)
4044     {
4045         this.valid = true;
4046         this.fireEvent('enter', dd, e, data);
4047         if(this.overClass){
4048             this.el.addClass(this.overClass);
4049         }
4050         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051             this.valid ? this.dropAllowed : this.dropNotAllowed
4052         );
4053     },
4054
4055     /**
4056      * @hide
4057      */
4058     notifyOver : function(dd, e, data)
4059     {
4060         this.valid = true;
4061         this.fireEvent('over', dd, e, data);
4062         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063             this.valid ? this.dropAllowed : this.dropNotAllowed
4064         );
4065     },
4066
4067     /**
4068      * @hide
4069      */
4070     notifyOut : function(dd, e, data)
4071     {
4072         this.fireEvent('out', dd, e, data);
4073         if(this.overClass){
4074             this.el.removeClass(this.overClass);
4075         }
4076     },
4077
4078     /**
4079      * @hide
4080      */
4081     notifyDrop : function(dd, e, data)
4082     {
4083         this.success = false;
4084         this.fireEvent('drop', dd, e, data);
4085         return this.success;
4086     }
4087 });/*
4088  * Based on:
4089  * Ext JS Library 1.1.1
4090  * Copyright(c) 2006-2007, Ext JS, LLC.
4091  *
4092  * Originally Released Under LGPL - original licence link has changed is not relivant.
4093  *
4094  * Fork - LGPL
4095  * <script type="text/javascript">
4096  */
4097
4098
4099 /**
4100  * @class Roo.dd.DragZone
4101  * @extends Roo.dd.DragSource
4102  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4104  * @constructor
4105  * @param {String/HTMLElement/Element} el The container element
4106  * @param {Object} config
4107  */
4108 Roo.dd.DragZone = function(el, config){
4109     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110     if(this.containerScroll){
4111         Roo.dd.ScrollManager.register(this.el);
4112     }
4113 };
4114
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4116     /**
4117      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118      * for auto scrolling during drag operations.
4119      */
4120     /**
4121      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122      * method after a failed drop (defaults to "c3daf9" - light blue)
4123      */
4124
4125     /**
4126      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127      * for a valid target to drag based on the mouse down. Override this method
4128      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130      * @param {EventObject} e The mouse down event
4131      * @return {Object} The dragData
4132      */
4133     getDragData : function(e){
4134         return Roo.dd.Registry.getHandleFromEvent(e);
4135     },
4136     
4137     /**
4138      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139      * this.dragData.ddel
4140      * @param {Number} x The x position of the click on the dragged object
4141      * @param {Number} y The y position of the click on the dragged object
4142      * @return {Boolean} true to continue the drag, false to cancel
4143      */
4144     onInitDrag : function(x, y){
4145         this.proxy.update(this.dragData.ddel.cloneNode(true));
4146         this.onStartDrag(x, y);
4147         return true;
4148     },
4149     
4150     /**
4151      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4152      */
4153     afterRepair : function(){
4154         if(Roo.enableFx){
4155             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4156         }
4157         this.dragging = false;
4158     },
4159
4160     /**
4161      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162      * the XY of this.dragData.ddel
4163      * @param {EventObject} e The mouse up event
4164      * @return {Array} The xy location (e.g. [100, 200])
4165      */
4166     getRepairXY : function(e){
4167         return Roo.Element.fly(this.dragData.ddel).getXY();  
4168     }
4169 });/*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179 /**
4180  * @class Roo.dd.DropZone
4181  * @extends Roo.dd.DropTarget
4182  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4184  * @constructor
4185  * @param {String/HTMLElement/Element} el The container element
4186  * @param {Object} config
4187  */
4188 Roo.dd.DropZone = function(el, config){
4189     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4190 };
4191
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4193     /**
4194      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4195      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196      * provide your own custom lookup.
4197      * @param {Event} e The event
4198      * @return {Object} data The custom data
4199      */
4200     getTargetFromEvent : function(e){
4201         return Roo.dd.Registry.getTargetFromEvent(e);
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206      * that it has registered.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeEnter : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4221      * overridden to provide the proper feedback.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228      * underlying {@link Roo.dd.StatusProxy} can be updated
4229      */
4230     onNodeOver : function(n, dd, e, data){
4231         return this.dropAllowed;
4232     },
4233
4234     /**
4235      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4237      * node-specific processing if necessary.
4238      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239      * {@link #getTargetFromEvent} for this node)
4240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241      * @param {Event} e The event
4242      * @param {Object} data An object containing arbitrary data supplied by the drag source
4243      */
4244     onNodeOut : function(n, dd, e, data){
4245         
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4251      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253      * {@link #getTargetFromEvent} for this node)
4254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255      * @param {Event} e The event
4256      * @param {Object} data An object containing arbitrary data supplied by the drag source
4257      * @return {Boolean} True if the drop was valid, else false
4258      */
4259     onNodeDrop : function(n, dd, e, data){
4260         return false;
4261     },
4262
4263     /**
4264      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4266      * it should be overridden to provide the proper feedback if necessary.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     onContainerOver : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4280      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283      * @param {Event} e The event
4284      * @param {Object} data An object containing arbitrary data supplied by the drag source
4285      * @return {Boolean} True if the drop was valid, else false
4286      */
4287     onContainerDrop : function(dd, e, data){
4288         return false;
4289     },
4290
4291     /**
4292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4294      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295      * you should override this method and provide a custom implementation.
4296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297      * @param {Event} e The event
4298      * @param {Object} data An object containing arbitrary data supplied by the drag source
4299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300      * underlying {@link Roo.dd.StatusProxy} can be updated
4301      */
4302     notifyEnter : function(dd, e, data){
4303         return this.dropNotAllowed;
4304     },
4305
4306     /**
4307      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308      * This method will be called on every mouse movement while the drag source is over the drop zone.
4309      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312      * registered node, it will call {@link #onContainerOver}.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag source
4316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317      * underlying {@link Roo.dd.StatusProxy} can be updated
4318      */
4319     notifyOver : function(dd, e, data){
4320         var n = this.getTargetFromEvent(e);
4321         if(!n){ // not over valid drop target
4322             if(this.lastOverNode){
4323                 this.onNodeOut(this.lastOverNode, dd, e, data);
4324                 this.lastOverNode = null;
4325             }
4326             return this.onContainerOver(dd, e, data);
4327         }
4328         if(this.lastOverNode != n){
4329             if(this.lastOverNode){
4330                 this.onNodeOut(this.lastOverNode, dd, e, data);
4331             }
4332             this.onNodeEnter(n, dd, e, data);
4333             this.lastOverNode = n;
4334         }
4335         return this.onNodeOver(n, dd, e, data);
4336     },
4337
4338     /**
4339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4341      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343      * @param {Event} e The event
4344      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4345      */
4346     notifyOut : function(dd, e, data){
4347         if(this.lastOverNode){
4348             this.onNodeOut(this.lastOverNode, dd, e, data);
4349             this.lastOverNode = null;
4350         }
4351     },
4352
4353     /**
4354      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4356      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357      * otherwise it will call {@link #onContainerDrop}.
4358      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359      * @param {Event} e The event
4360      * @param {Object} data An object containing arbitrary data supplied by the drag source
4361      * @return {Boolean} True if the drop was valid, else false
4362      */
4363     notifyDrop : function(dd, e, data){
4364         if(this.lastOverNode){
4365             this.onNodeOut(this.lastOverNode, dd, e, data);
4366             this.lastOverNode = null;
4367         }
4368         var n = this.getTargetFromEvent(e);
4369         return n ?
4370             this.onNodeDrop(n, dd, e, data) :
4371             this.onContainerDrop(dd, e, data);
4372     },
4373
4374     // private
4375     triggerCacheRefresh : function(){
4376         Roo.dd.DDM.refreshCache(this.groups);
4377     }  
4378 });/*
4379  * Based on:
4380  * Ext JS Library 1.1.1
4381  * Copyright(c) 2006-2007, Ext JS, LLC.
4382  *
4383  * Originally Released Under LGPL - original licence link has changed is not relivant.
4384  *
4385  * Fork - LGPL
4386  * <script type="text/javascript">
4387  */
4388
4389
4390 /**
4391  * @class Roo.data.SortTypes
4392  * @singleton
4393  * Defines the default sorting (casting?) comparison functions used when sorting data.
4394  */
4395 Roo.data.SortTypes = {
4396     /**
4397      * Default sort that does nothing
4398      * @param {Mixed} s The value being converted
4399      * @return {Mixed} The comparison value
4400      */
4401     none : function(s){
4402         return s;
4403     },
4404     
4405     /**
4406      * The regular expression used to strip tags
4407      * @type {RegExp}
4408      * @property
4409      */
4410     stripTagsRE : /<\/?[^>]+>/gi,
4411     
4412     /**
4413      * Strips all HTML tags to sort on text only
4414      * @param {Mixed} s The value being converted
4415      * @return {String} The comparison value
4416      */
4417     asText : function(s){
4418         return String(s).replace(this.stripTagsRE, "");
4419     },
4420     
4421     /**
4422      * Strips all HTML tags to sort on text only - Case insensitive
4423      * @param {Mixed} s The value being converted
4424      * @return {String} The comparison value
4425      */
4426     asUCText : function(s){
4427         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4428     },
4429     
4430     /**
4431      * Case insensitive string
4432      * @param {Mixed} s The value being converted
4433      * @return {String} The comparison value
4434      */
4435     asUCString : function(s) {
4436         return String(s).toUpperCase();
4437     },
4438     
4439     /**
4440      * Date sorting
4441      * @param {Mixed} s The value being converted
4442      * @return {Number} The comparison value
4443      */
4444     asDate : function(s) {
4445         if(!s){
4446             return 0;
4447         }
4448         if(s instanceof Date){
4449             return s.getTime();
4450         }
4451         return Date.parse(String(s));
4452     },
4453     
4454     /**
4455      * Float sorting
4456      * @param {Mixed} s The value being converted
4457      * @return {Float} The comparison value
4458      */
4459     asFloat : function(s) {
4460         var val = parseFloat(String(s).replace(/,/g, ""));
4461         if(isNaN(val)) val = 0;
4462         return val;
4463     },
4464     
4465     /**
4466      * Integer sorting
4467      * @param {Mixed} s The value being converted
4468      * @return {Number} The comparison value
4469      */
4470     asInt : function(s) {
4471         var val = parseInt(String(s).replace(/,/g, ""));
4472         if(isNaN(val)) val = 0;
4473         return val;
4474     }
4475 };/*
4476  * Based on:
4477  * Ext JS Library 1.1.1
4478  * Copyright(c) 2006-2007, Ext JS, LLC.
4479  *
4480  * Originally Released Under LGPL - original licence link has changed is not relivant.
4481  *
4482  * Fork - LGPL
4483  * <script type="text/javascript">
4484  */
4485
4486 /**
4487 * @class Roo.data.Record
4488  * Instances of this class encapsulate both record <em>definition</em> information, and record
4489  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4490  * to access Records cached in an {@link Roo.data.Store} object.<br>
4491  * <p>
4492  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4493  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4494  * objects.<br>
4495  * <p>
4496  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4497  * @constructor
4498  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4499  * {@link #create}. The parameters are the same.
4500  * @param {Array} data An associative Array of data values keyed by the field name.
4501  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4502  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4503  * not specified an integer id is generated.
4504  */
4505 Roo.data.Record = function(data, id){
4506     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4507     this.data = data;
4508 };
4509
4510 /**
4511  * Generate a constructor for a specific record layout.
4512  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4513  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4514  * Each field definition object may contain the following properties: <ul>
4515  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4516  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4517  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4518  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4519  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4520  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4521  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4522  * this may be omitted.</p></li>
4523  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4524  * <ul><li>auto (Default, implies no conversion)</li>
4525  * <li>string</li>
4526  * <li>int</li>
4527  * <li>float</li>
4528  * <li>boolean</li>
4529  * <li>date</li></ul></p></li>
4530  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4531  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4532  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4533  * by the Reader into an object that will be stored in the Record. It is passed the
4534  * following parameters:<ul>
4535  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4536  * </ul></p></li>
4537  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4538  * </ul>
4539  * <br>usage:<br><pre><code>
4540 var TopicRecord = Roo.data.Record.create(
4541     {name: 'title', mapping: 'topic_title'},
4542     {name: 'author', mapping: 'username'},
4543     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4544     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4545     {name: 'lastPoster', mapping: 'user2'},
4546     {name: 'excerpt', mapping: 'post_text'}
4547 );
4548
4549 var myNewRecord = new TopicRecord({
4550     title: 'Do my job please',
4551     author: 'noobie',
4552     totalPosts: 1,
4553     lastPost: new Date(),
4554     lastPoster: 'Animal',
4555     excerpt: 'No way dude!'
4556 });
4557 myStore.add(myNewRecord);
4558 </code></pre>
4559  * @method create
4560  * @static
4561  */
4562 Roo.data.Record.create = function(o){
4563     var f = function(){
4564         f.superclass.constructor.apply(this, arguments);
4565     };
4566     Roo.extend(f, Roo.data.Record);
4567     var p = f.prototype;
4568     p.fields = new Roo.util.MixedCollection(false, function(field){
4569         return field.name;
4570     });
4571     for(var i = 0, len = o.length; i < len; i++){
4572         p.fields.add(new Roo.data.Field(o[i]));
4573     }
4574     f.getField = function(name){
4575         return p.fields.get(name);  
4576     };
4577     return f;
4578 };
4579
4580 Roo.data.Record.AUTO_ID = 1000;
4581 Roo.data.Record.EDIT = 'edit';
4582 Roo.data.Record.REJECT = 'reject';
4583 Roo.data.Record.COMMIT = 'commit';
4584
4585 Roo.data.Record.prototype = {
4586     /**
4587      * Readonly flag - true if this record has been modified.
4588      * @type Boolean
4589      */
4590     dirty : false,
4591     editing : false,
4592     error: null,
4593     modified: null,
4594
4595     // private
4596     join : function(store){
4597         this.store = store;
4598     },
4599
4600     /**
4601      * Set the named field to the specified value.
4602      * @param {String} name The name of the field to set.
4603      * @param {Object} value The value to set the field to.
4604      */
4605     set : function(name, value){
4606         if(this.data[name] == value){
4607             return;
4608         }
4609         this.dirty = true;
4610         if(!this.modified){
4611             this.modified = {};
4612         }
4613         if(typeof this.modified[name] == 'undefined'){
4614             this.modified[name] = this.data[name];
4615         }
4616         this.data[name] = value;
4617         if(!this.editing && this.store){
4618             this.store.afterEdit(this);
4619         }       
4620     },
4621
4622     /**
4623      * Get the value of the named field.
4624      * @param {String} name The name of the field to get the value of.
4625      * @return {Object} The value of the field.
4626      */
4627     get : function(name){
4628         return this.data[name]; 
4629     },
4630
4631     // private
4632     beginEdit : function(){
4633         this.editing = true;
4634         this.modified = {}; 
4635     },
4636
4637     // private
4638     cancelEdit : function(){
4639         this.editing = false;
4640         delete this.modified;
4641     },
4642
4643     // private
4644     endEdit : function(){
4645         this.editing = false;
4646         if(this.dirty && this.store){
4647             this.store.afterEdit(this);
4648         }
4649     },
4650
4651     /**
4652      * Usually called by the {@link Roo.data.Store} which owns the Record.
4653      * Rejects all changes made to the Record since either creation, or the last commit operation.
4654      * Modified fields are reverted to their original values.
4655      * <p>
4656      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4657      * of reject operations.
4658      */
4659     reject : function(){
4660         var m = this.modified;
4661         for(var n in m){
4662             if(typeof m[n] != "function"){
4663                 this.data[n] = m[n];
4664             }
4665         }
4666         this.dirty = false;
4667         delete this.modified;
4668         this.editing = false;
4669         if(this.store){
4670             this.store.afterReject(this);
4671         }
4672     },
4673
4674     /**
4675      * Usually called by the {@link Roo.data.Store} which owns the Record.
4676      * Commits all changes made to the Record since either creation, or the last commit operation.
4677      * <p>
4678      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4679      * of commit operations.
4680      */
4681     commit : function(){
4682         this.dirty = false;
4683         delete this.modified;
4684         this.editing = false;
4685         if(this.store){
4686             this.store.afterCommit(this);
4687         }
4688     },
4689
4690     // private
4691     hasError : function(){
4692         return this.error != null;
4693     },
4694
4695     // private
4696     clearError : function(){
4697         this.error = null;
4698     },
4699
4700     /**
4701      * Creates a copy of this record.
4702      * @param {String} id (optional) A new record id if you don't want to use this record's id
4703      * @return {Record}
4704      */
4705     copy : function(newId) {
4706         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4707     }
4708 };/*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718
4719
4720
4721 /**
4722  * @class Roo.data.Store
4723  * @extends Roo.util.Observable
4724  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4725  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4726  * <p>
4727  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4728  * has no knowledge of the format of the data returned by the Proxy.<br>
4729  * <p>
4730  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4731  * instances from the data object. These records are cached and made available through accessor functions.
4732  * @constructor
4733  * Creates a new Store.
4734  * @param {Object} config A config object containing the objects needed for the Store to access data,
4735  * and read the data into Records.
4736  */
4737 Roo.data.Store = function(config){
4738     this.data = new Roo.util.MixedCollection(false);
4739     this.data.getKey = function(o){
4740         return o.id;
4741     };
4742     this.baseParams = {};
4743     // private
4744     this.paramNames = {
4745         "start" : "start",
4746         "limit" : "limit",
4747         "sort" : "sort",
4748         "dir" : "dir",
4749         "multisort" : "_multisort"
4750     };
4751
4752     if(config && config.data){
4753         this.inlineData = config.data;
4754         delete config.data;
4755     }
4756
4757     Roo.apply(this, config);
4758     
4759     if(this.reader){ // reader passed
4760         this.reader = Roo.factory(this.reader, Roo.data);
4761         this.reader.xmodule = this.xmodule || false;
4762         if(!this.recordType){
4763             this.recordType = this.reader.recordType;
4764         }
4765         if(this.reader.onMetaChange){
4766             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4767         }
4768     }
4769
4770     if(this.recordType){
4771         this.fields = this.recordType.prototype.fields;
4772     }
4773     this.modified = [];
4774
4775     this.addEvents({
4776         /**
4777          * @event datachanged
4778          * Fires when the data cache has changed, and a widget which is using this Store
4779          * as a Record cache should refresh its view.
4780          * @param {Store} this
4781          */
4782         datachanged : true,
4783         /**
4784          * @event metachange
4785          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4786          * @param {Store} this
4787          * @param {Object} meta The JSON metadata
4788          */
4789         metachange : true,
4790         /**
4791          * @event add
4792          * Fires when Records have been added to the Store
4793          * @param {Store} this
4794          * @param {Roo.data.Record[]} records The array of Records added
4795          * @param {Number} index The index at which the record(s) were added
4796          */
4797         add : true,
4798         /**
4799          * @event remove
4800          * Fires when a Record has been removed from the Store
4801          * @param {Store} this
4802          * @param {Roo.data.Record} record The Record that was removed
4803          * @param {Number} index The index at which the record was removed
4804          */
4805         remove : true,
4806         /**
4807          * @event update
4808          * Fires when a Record has been updated
4809          * @param {Store} this
4810          * @param {Roo.data.Record} record The Record that was updated
4811          * @param {String} operation The update operation being performed.  Value may be one of:
4812          * <pre><code>
4813  Roo.data.Record.EDIT
4814  Roo.data.Record.REJECT
4815  Roo.data.Record.COMMIT
4816          * </code></pre>
4817          */
4818         update : true,
4819         /**
4820          * @event clear
4821          * Fires when the data cache has been cleared.
4822          * @param {Store} this
4823          */
4824         clear : true,
4825         /**
4826          * @event beforeload
4827          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4828          * the load action will be canceled.
4829          * @param {Store} this
4830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4831          */
4832         beforeload : true,
4833         /**
4834          * @event beforeloadadd
4835          * Fires after a new set of Records has been loaded.
4836          * @param {Store} this
4837          * @param {Roo.data.Record[]} records The Records that were loaded
4838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4839          */
4840         beforeloadadd : true,
4841         /**
4842          * @event load
4843          * Fires after a new set of Records has been loaded, before they are added to the store.
4844          * @param {Store} this
4845          * @param {Roo.data.Record[]} records The Records that were loaded
4846          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4847          * @params {Object} return from reader
4848          */
4849         load : true,
4850         /**
4851          * @event loadexception
4852          * Fires if an exception occurs in the Proxy during loading.
4853          * Called with the signature of the Proxy's "loadexception" event.
4854          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4855          * 
4856          * @param {Proxy} 
4857          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4858          * @param {Object} load options 
4859          * @param {Object} jsonData from your request (normally this contains the Exception)
4860          */
4861         loadexception : true
4862     });
4863     
4864     if(this.proxy){
4865         this.proxy = Roo.factory(this.proxy, Roo.data);
4866         this.proxy.xmodule = this.xmodule || false;
4867         this.relayEvents(this.proxy,  ["loadexception"]);
4868     }
4869     this.sortToggle = {};
4870     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4871
4872     Roo.data.Store.superclass.constructor.call(this);
4873
4874     if(this.inlineData){
4875         this.loadData(this.inlineData);
4876         delete this.inlineData;
4877     }
4878 };
4879
4880 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4881      /**
4882     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4883     * without a remote query - used by combo/forms at present.
4884     */
4885     
4886     /**
4887     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4888     */
4889     /**
4890     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4891     */
4892     /**
4893     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4894     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4895     */
4896     /**
4897     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4898     * on any HTTP request
4899     */
4900     /**
4901     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4902     */
4903     /**
4904     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4905     */
4906     multiSort: false,
4907     /**
4908     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4909     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4910     */
4911     remoteSort : false,
4912
4913     /**
4914     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4915      * loaded or when a record is removed. (defaults to false).
4916     */
4917     pruneModifiedRecords : false,
4918
4919     // private
4920     lastOptions : null,
4921
4922     /**
4923      * Add Records to the Store and fires the add event.
4924      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4925      */
4926     add : function(records){
4927         records = [].concat(records);
4928         for(var i = 0, len = records.length; i < len; i++){
4929             records[i].join(this);
4930         }
4931         var index = this.data.length;
4932         this.data.addAll(records);
4933         this.fireEvent("add", this, records, index);
4934     },
4935
4936     /**
4937      * Remove a Record from the Store and fires the remove event.
4938      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4939      */
4940     remove : function(record){
4941         var index = this.data.indexOf(record);
4942         this.data.removeAt(index);
4943         if(this.pruneModifiedRecords){
4944             this.modified.remove(record);
4945         }
4946         this.fireEvent("remove", this, record, index);
4947     },
4948
4949     /**
4950      * Remove all Records from the Store and fires the clear event.
4951      */
4952     removeAll : function(){
4953         this.data.clear();
4954         if(this.pruneModifiedRecords){
4955             this.modified = [];
4956         }
4957         this.fireEvent("clear", this);
4958     },
4959
4960     /**
4961      * Inserts Records to the Store at the given index and fires the add event.
4962      * @param {Number} index The start index at which to insert the passed Records.
4963      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4964      */
4965     insert : function(index, records){
4966         records = [].concat(records);
4967         for(var i = 0, len = records.length; i < len; i++){
4968             this.data.insert(index, records[i]);
4969             records[i].join(this);
4970         }
4971         this.fireEvent("add", this, records, index);
4972     },
4973
4974     /**
4975      * Get the index within the cache of the passed Record.
4976      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4977      * @return {Number} The index of the passed Record. Returns -1 if not found.
4978      */
4979     indexOf : function(record){
4980         return this.data.indexOf(record);
4981     },
4982
4983     /**
4984      * Get the index within the cache of the Record with the passed id.
4985      * @param {String} id The id of the Record to find.
4986      * @return {Number} The index of the Record. Returns -1 if not found.
4987      */
4988     indexOfId : function(id){
4989         return this.data.indexOfKey(id);
4990     },
4991
4992     /**
4993      * Get the Record with the specified id.
4994      * @param {String} id The id of the Record to find.
4995      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4996      */
4997     getById : function(id){
4998         return this.data.key(id);
4999     },
5000
5001     /**
5002      * Get the Record at the specified index.
5003      * @param {Number} index The index of the Record to find.
5004      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5005      */
5006     getAt : function(index){
5007         return this.data.itemAt(index);
5008     },
5009
5010     /**
5011      * Returns a range of Records between specified indices.
5012      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5013      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5014      * @return {Roo.data.Record[]} An array of Records
5015      */
5016     getRange : function(start, end){
5017         return this.data.getRange(start, end);
5018     },
5019
5020     // private
5021     storeOptions : function(o){
5022         o = Roo.apply({}, o);
5023         delete o.callback;
5024         delete o.scope;
5025         this.lastOptions = o;
5026     },
5027
5028     /**
5029      * Loads the Record cache from the configured Proxy using the configured Reader.
5030      * <p>
5031      * If using remote paging, then the first load call must specify the <em>start</em>
5032      * and <em>limit</em> properties in the options.params property to establish the initial
5033      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5034      * <p>
5035      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5036      * and this call will return before the new data has been loaded. Perform any post-processing
5037      * in a callback function, or in a "load" event handler.</strong>
5038      * <p>
5039      * @param {Object} options An object containing properties which control loading options:<ul>
5040      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5041      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5042      * passed the following arguments:<ul>
5043      * <li>r : Roo.data.Record[]</li>
5044      * <li>options: Options object from the load call</li>
5045      * <li>success: Boolean success indicator</li></ul></li>
5046      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5047      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5048      * </ul>
5049      */
5050     load : function(options){
5051         options = options || {};
5052         if(this.fireEvent("beforeload", this, options) !== false){
5053             this.storeOptions(options);
5054             var p = Roo.apply(options.params || {}, this.baseParams);
5055             // if meta was not loaded from remote source.. try requesting it.
5056             if (!this.reader.metaFromRemote) {
5057                 p._requestMeta = 1;
5058             }
5059             if(this.sortInfo && this.remoteSort){
5060                 var pn = this.paramNames;
5061                 p[pn["sort"]] = this.sortInfo.field;
5062                 p[pn["dir"]] = this.sortInfo.direction;
5063             }
5064             if (this.multiSort) {
5065                 var pn = this.paramNames;
5066                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5067             }
5068             
5069             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5070         }
5071     },
5072
5073     /**
5074      * Reloads the Record cache from the configured Proxy using the configured Reader and
5075      * the options from the last load operation performed.
5076      * @param {Object} options (optional) An object containing properties which may override the options
5077      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5078      * the most recently used options are reused).
5079      */
5080     reload : function(options){
5081         this.load(Roo.applyIf(options||{}, this.lastOptions));
5082     },
5083
5084     // private
5085     // Called as a callback by the Reader during a load operation.
5086     loadRecords : function(o, options, success){
5087         if(!o || success === false){
5088             if(success !== false){
5089                 this.fireEvent("load", this, [], options, o);
5090             }
5091             if(options.callback){
5092                 options.callback.call(options.scope || this, [], options, false);
5093             }
5094             return;
5095         }
5096         // if data returned failure - throw an exception.
5097         if (o.success === false) {
5098             // show a message if no listener is registered.
5099             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5100                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5101             }
5102             // loadmask wil be hooked into this..
5103             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5104             return;
5105         }
5106         var r = o.records, t = o.totalRecords || r.length;
5107         
5108         this.fireEvent("beforeloadadd", this, r, options, o);
5109         
5110         if(!options || options.add !== true){
5111             if(this.pruneModifiedRecords){
5112                 this.modified = [];
5113             }
5114             for(var i = 0, len = r.length; i < len; i++){
5115                 r[i].join(this);
5116             }
5117             if(this.snapshot){
5118                 this.data = this.snapshot;
5119                 delete this.snapshot;
5120             }
5121             this.data.clear();
5122             this.data.addAll(r);
5123             this.totalLength = t;
5124             this.applySort();
5125             this.fireEvent("datachanged", this);
5126         }else{
5127             this.totalLength = Math.max(t, this.data.length+r.length);
5128             this.add(r);
5129         }
5130         this.fireEvent("load", this, r, options, o);
5131         if(options.callback){
5132             options.callback.call(options.scope || this, r, options, true);
5133         }
5134     },
5135
5136
5137     /**
5138      * Loads data from a passed data block. A Reader which understands the format of the data
5139      * must have been configured in the constructor.
5140      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5141      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5142      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5143      */
5144     loadData : function(o, append){
5145         var r = this.reader.readRecords(o);
5146         this.loadRecords(r, {add: append}, true);
5147     },
5148
5149     /**
5150      * Gets the number of cached records.
5151      * <p>
5152      * <em>If using paging, this may not be the total size of the dataset. If the data object
5153      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5154      * the data set size</em>
5155      */
5156     getCount : function(){
5157         return this.data.length || 0;
5158     },
5159
5160     /**
5161      * Gets the total number of records in the dataset as returned by the server.
5162      * <p>
5163      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5164      * the dataset size</em>
5165      */
5166     getTotalCount : function(){
5167         return this.totalLength || 0;
5168     },
5169
5170     /**
5171      * Returns the sort state of the Store as an object with two properties:
5172      * <pre><code>
5173  field {String} The name of the field by which the Records are sorted
5174  direction {String} The sort order, "ASC" or "DESC"
5175      * </code></pre>
5176      */
5177     getSortState : function(){
5178         return this.sortInfo;
5179     },
5180
5181     // private
5182     applySort : function(){
5183         if(this.sortInfo && !this.remoteSort){
5184             var s = this.sortInfo, f = s.field;
5185             var st = this.fields.get(f).sortType;
5186             var fn = function(r1, r2){
5187                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5188                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5189             };
5190             this.data.sort(s.direction, fn);
5191             if(this.snapshot && this.snapshot != this.data){
5192                 this.snapshot.sort(s.direction, fn);
5193             }
5194         }
5195     },
5196
5197     /**
5198      * Sets the default sort column and order to be used by the next load operation.
5199      * @param {String} fieldName The name of the field to sort by.
5200      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5201      */
5202     setDefaultSort : function(field, dir){
5203         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5204     },
5205
5206     /**
5207      * Sort the Records.
5208      * If remote sorting is used, the sort is performed on the server, and the cache is
5209      * reloaded. If local sorting is used, the cache is sorted internally.
5210      * @param {String} fieldName The name of the field to sort by.
5211      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5212      */
5213     sort : function(fieldName, dir){
5214         var f = this.fields.get(fieldName);
5215         if(!dir){
5216             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5217             
5218             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5219                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5220             }else{
5221                 dir = f.sortDir;
5222             }
5223         }
5224         this.sortToggle[f.name] = dir;
5225         this.sortInfo = {field: f.name, direction: dir};
5226         if(!this.remoteSort){
5227             this.applySort();
5228             this.fireEvent("datachanged", this);
5229         }else{
5230             this.load(this.lastOptions);
5231         }
5232     },
5233
5234     /**
5235      * Calls the specified function for each of the Records in the cache.
5236      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5237      * Returning <em>false</em> aborts and exits the iteration.
5238      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5239      */
5240     each : function(fn, scope){
5241         this.data.each(fn, scope);
5242     },
5243
5244     /**
5245      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5246      * (e.g., during paging).
5247      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5248      */
5249     getModifiedRecords : function(){
5250         return this.modified;
5251     },
5252
5253     // private
5254     createFilterFn : function(property, value, anyMatch){
5255         if(!value.exec){ // not a regex
5256             value = String(value);
5257             if(value.length == 0){
5258                 return false;
5259             }
5260             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5261         }
5262         return function(r){
5263             return value.test(r.data[property]);
5264         };
5265     },
5266
5267     /**
5268      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5269      * @param {String} property A field on your records
5270      * @param {Number} start The record index to start at (defaults to 0)
5271      * @param {Number} end The last record index to include (defaults to length - 1)
5272      * @return {Number} The sum
5273      */
5274     sum : function(property, start, end){
5275         var rs = this.data.items, v = 0;
5276         start = start || 0;
5277         end = (end || end === 0) ? end : rs.length-1;
5278
5279         for(var i = start; i <= end; i++){
5280             v += (rs[i].data[property] || 0);
5281         }
5282         return v;
5283     },
5284
5285     /**
5286      * Filter the records by a specified property.
5287      * @param {String} field A field on your records
5288      * @param {String/RegExp} value Either a string that the field
5289      * should start with or a RegExp to test against the field
5290      * @param {Boolean} anyMatch True to match any part not just the beginning
5291      */
5292     filter : function(property, value, anyMatch){
5293         var fn = this.createFilterFn(property, value, anyMatch);
5294         return fn ? this.filterBy(fn) : this.clearFilter();
5295     },
5296
5297     /**
5298      * Filter by a function. The specified function will be called with each
5299      * record in this data source. If the function returns true the record is included,
5300      * otherwise it is filtered.
5301      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5302      * @param {Object} scope (optional) The scope of the function (defaults to this)
5303      */
5304     filterBy : function(fn, scope){
5305         this.snapshot = this.snapshot || this.data;
5306         this.data = this.queryBy(fn, scope||this);
5307         this.fireEvent("datachanged", this);
5308     },
5309
5310     /**
5311      * Query the records by a specified property.
5312      * @param {String} field A field on your records
5313      * @param {String/RegExp} value Either a string that the field
5314      * should start with or a RegExp to test against the field
5315      * @param {Boolean} anyMatch True to match any part not just the beginning
5316      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5317      */
5318     query : function(property, value, anyMatch){
5319         var fn = this.createFilterFn(property, value, anyMatch);
5320         return fn ? this.queryBy(fn) : this.data.clone();
5321     },
5322
5323     /**
5324      * Query by a function. The specified function will be called with each
5325      * record in this data source. If the function returns true the record is included
5326      * in the results.
5327      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5328      * @param {Object} scope (optional) The scope of the function (defaults to this)
5329       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5330      **/
5331     queryBy : function(fn, scope){
5332         var data = this.snapshot || this.data;
5333         return data.filterBy(fn, scope||this);
5334     },
5335
5336     /**
5337      * Collects unique values for a particular dataIndex from this store.
5338      * @param {String} dataIndex The property to collect
5339      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5340      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5341      * @return {Array} An array of the unique values
5342      **/
5343     collect : function(dataIndex, allowNull, bypassFilter){
5344         var d = (bypassFilter === true && this.snapshot) ?
5345                 this.snapshot.items : this.data.items;
5346         var v, sv, r = [], l = {};
5347         for(var i = 0, len = d.length; i < len; i++){
5348             v = d[i].data[dataIndex];
5349             sv = String(v);
5350             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5351                 l[sv] = true;
5352                 r[r.length] = v;
5353             }
5354         }
5355         return r;
5356     },
5357
5358     /**
5359      * Revert to a view of the Record cache with no filtering applied.
5360      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5361      */
5362     clearFilter : function(suppressEvent){
5363         if(this.snapshot && this.snapshot != this.data){
5364             this.data = this.snapshot;
5365             delete this.snapshot;
5366             if(suppressEvent !== true){
5367                 this.fireEvent("datachanged", this);
5368             }
5369         }
5370     },
5371
5372     // private
5373     afterEdit : function(record){
5374         if(this.modified.indexOf(record) == -1){
5375             this.modified.push(record);
5376         }
5377         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5378     },
5379     
5380     // private
5381     afterReject : function(record){
5382         this.modified.remove(record);
5383         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5384     },
5385
5386     // private
5387     afterCommit : function(record){
5388         this.modified.remove(record);
5389         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5390     },
5391
5392     /**
5393      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5394      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5395      */
5396     commitChanges : function(){
5397         var m = this.modified.slice(0);
5398         this.modified = [];
5399         for(var i = 0, len = m.length; i < len; i++){
5400             m[i].commit();
5401         }
5402     },
5403
5404     /**
5405      * Cancel outstanding changes on all changed records.
5406      */
5407     rejectChanges : function(){
5408         var m = this.modified.slice(0);
5409         this.modified = [];
5410         for(var i = 0, len = m.length; i < len; i++){
5411             m[i].reject();
5412         }
5413     },
5414
5415     onMetaChange : function(meta, rtype, o){
5416         this.recordType = rtype;
5417         this.fields = rtype.prototype.fields;
5418         delete this.snapshot;
5419         this.sortInfo = meta.sortInfo || this.sortInfo;
5420         this.modified = [];
5421         this.fireEvent('metachange', this, this.reader.meta);
5422     },
5423     
5424     moveIndex : function(data, type)
5425     {
5426         var index = this.indexOf(data);
5427         
5428         var newIndex = index + type;
5429         
5430         this.remove(data);
5431         
5432         this.insert(newIndex, data);
5433         
5434     }
5435 });/*
5436  * Based on:
5437  * Ext JS Library 1.1.1
5438  * Copyright(c) 2006-2007, Ext JS, LLC.
5439  *
5440  * Originally Released Under LGPL - original licence link has changed is not relivant.
5441  *
5442  * Fork - LGPL
5443  * <script type="text/javascript">
5444  */
5445
5446 /**
5447  * @class Roo.data.SimpleStore
5448  * @extends Roo.data.Store
5449  * Small helper class to make creating Stores from Array data easier.
5450  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5451  * @cfg {Array} fields An array of field definition objects, or field name strings.
5452  * @cfg {Array} data The multi-dimensional array of data
5453  * @constructor
5454  * @param {Object} config
5455  */
5456 Roo.data.SimpleStore = function(config){
5457     Roo.data.SimpleStore.superclass.constructor.call(this, {
5458         isLocal : true,
5459         reader: new Roo.data.ArrayReader({
5460                 id: config.id
5461             },
5462             Roo.data.Record.create(config.fields)
5463         ),
5464         proxy : new Roo.data.MemoryProxy(config.data)
5465     });
5466     this.load();
5467 };
5468 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5469  * Based on:
5470  * Ext JS Library 1.1.1
5471  * Copyright(c) 2006-2007, Ext JS, LLC.
5472  *
5473  * Originally Released Under LGPL - original licence link has changed is not relivant.
5474  *
5475  * Fork - LGPL
5476  * <script type="text/javascript">
5477  */
5478
5479 /**
5480 /**
5481  * @extends Roo.data.Store
5482  * @class Roo.data.JsonStore
5483  * Small helper class to make creating Stores for JSON data easier. <br/>
5484 <pre><code>
5485 var store = new Roo.data.JsonStore({
5486     url: 'get-images.php',
5487     root: 'images',
5488     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5489 });
5490 </code></pre>
5491  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5492  * JsonReader and HttpProxy (unless inline data is provided).</b>
5493  * @cfg {Array} fields An array of field definition objects, or field name strings.
5494  * @constructor
5495  * @param {Object} config
5496  */
5497 Roo.data.JsonStore = function(c){
5498     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5499         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5500         reader: new Roo.data.JsonReader(c, c.fields)
5501     }));
5502 };
5503 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5504  * Based on:
5505  * Ext JS Library 1.1.1
5506  * Copyright(c) 2006-2007, Ext JS, LLC.
5507  *
5508  * Originally Released Under LGPL - original licence link has changed is not relivant.
5509  *
5510  * Fork - LGPL
5511  * <script type="text/javascript">
5512  */
5513
5514  
5515 Roo.data.Field = function(config){
5516     if(typeof config == "string"){
5517         config = {name: config};
5518     }
5519     Roo.apply(this, config);
5520     
5521     if(!this.type){
5522         this.type = "auto";
5523     }
5524     
5525     var st = Roo.data.SortTypes;
5526     // named sortTypes are supported, here we look them up
5527     if(typeof this.sortType == "string"){
5528         this.sortType = st[this.sortType];
5529     }
5530     
5531     // set default sortType for strings and dates
5532     if(!this.sortType){
5533         switch(this.type){
5534             case "string":
5535                 this.sortType = st.asUCString;
5536                 break;
5537             case "date":
5538                 this.sortType = st.asDate;
5539                 break;
5540             default:
5541                 this.sortType = st.none;
5542         }
5543     }
5544
5545     // define once
5546     var stripRe = /[\$,%]/g;
5547
5548     // prebuilt conversion function for this field, instead of
5549     // switching every time we're reading a value
5550     if(!this.convert){
5551         var cv, dateFormat = this.dateFormat;
5552         switch(this.type){
5553             case "":
5554             case "auto":
5555             case undefined:
5556                 cv = function(v){ return v; };
5557                 break;
5558             case "string":
5559                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5560                 break;
5561             case "int":
5562                 cv = function(v){
5563                     return v !== undefined && v !== null && v !== '' ?
5564                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5565                     };
5566                 break;
5567             case "float":
5568                 cv = function(v){
5569                     return v !== undefined && v !== null && v !== '' ?
5570                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5571                     };
5572                 break;
5573             case "bool":
5574             case "boolean":
5575                 cv = function(v){ return v === true || v === "true" || v == 1; };
5576                 break;
5577             case "date":
5578                 cv = function(v){
5579                     if(!v){
5580                         return '';
5581                     }
5582                     if(v instanceof Date){
5583                         return v;
5584                     }
5585                     if(dateFormat){
5586                         if(dateFormat == "timestamp"){
5587                             return new Date(v*1000);
5588                         }
5589                         return Date.parseDate(v, dateFormat);
5590                     }
5591                     var parsed = Date.parse(v);
5592                     return parsed ? new Date(parsed) : null;
5593                 };
5594              break;
5595             
5596         }
5597         this.convert = cv;
5598     }
5599 };
5600
5601 Roo.data.Field.prototype = {
5602     dateFormat: null,
5603     defaultValue: "",
5604     mapping: null,
5605     sortType : null,
5606     sortDir : "ASC"
5607 };/*
5608  * Based on:
5609  * Ext JS Library 1.1.1
5610  * Copyright(c) 2006-2007, Ext JS, LLC.
5611  *
5612  * Originally Released Under LGPL - original licence link has changed is not relivant.
5613  *
5614  * Fork - LGPL
5615  * <script type="text/javascript">
5616  */
5617  
5618 // Base class for reading structured data from a data source.  This class is intended to be
5619 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5620
5621 /**
5622  * @class Roo.data.DataReader
5623  * Base class for reading structured data from a data source.  This class is intended to be
5624  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5625  */
5626
5627 Roo.data.DataReader = function(meta, recordType){
5628     
5629     this.meta = meta;
5630     
5631     this.recordType = recordType instanceof Array ? 
5632         Roo.data.Record.create(recordType) : recordType;
5633 };
5634
5635 Roo.data.DataReader.prototype = {
5636      /**
5637      * Create an empty record
5638      * @param {Object} data (optional) - overlay some values
5639      * @return {Roo.data.Record} record created.
5640      */
5641     newRow :  function(d) {
5642         var da =  {};
5643         this.recordType.prototype.fields.each(function(c) {
5644             switch( c.type) {
5645                 case 'int' : da[c.name] = 0; break;
5646                 case 'date' : da[c.name] = new Date(); break;
5647                 case 'float' : da[c.name] = 0.0; break;
5648                 case 'boolean' : da[c.name] = false; break;
5649                 default : da[c.name] = ""; break;
5650             }
5651             
5652         });
5653         return new this.recordType(Roo.apply(da, d));
5654     }
5655     
5656 };/*
5657  * Based on:
5658  * Ext JS Library 1.1.1
5659  * Copyright(c) 2006-2007, Ext JS, LLC.
5660  *
5661  * Originally Released Under LGPL - original licence link has changed is not relivant.
5662  *
5663  * Fork - LGPL
5664  * <script type="text/javascript">
5665  */
5666
5667 /**
5668  * @class Roo.data.DataProxy
5669  * @extends Roo.data.Observable
5670  * This class is an abstract base class for implementations which provide retrieval of
5671  * unformatted data objects.<br>
5672  * <p>
5673  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5674  * (of the appropriate type which knows how to parse the data object) to provide a block of
5675  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5676  * <p>
5677  * Custom implementations must implement the load method as described in
5678  * {@link Roo.data.HttpProxy#load}.
5679  */
5680 Roo.data.DataProxy = function(){
5681     this.addEvents({
5682         /**
5683          * @event beforeload
5684          * Fires before a network request is made to retrieve a data object.
5685          * @param {Object} This DataProxy object.
5686          * @param {Object} params The params parameter to the load function.
5687          */
5688         beforeload : true,
5689         /**
5690          * @event load
5691          * Fires before the load method's callback is called.
5692          * @param {Object} This DataProxy object.
5693          * @param {Object} o The data object.
5694          * @param {Object} arg The callback argument object passed to the load function.
5695          */
5696         load : true,
5697         /**
5698          * @event loadexception
5699          * Fires if an Exception occurs during data retrieval.
5700          * @param {Object} This DataProxy object.
5701          * @param {Object} o The data object.
5702          * @param {Object} arg The callback argument object passed to the load function.
5703          * @param {Object} e The Exception.
5704          */
5705         loadexception : true
5706     });
5707     Roo.data.DataProxy.superclass.constructor.call(this);
5708 };
5709
5710 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5711
5712     /**
5713      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5714      */
5715 /*
5716  * Based on:
5717  * Ext JS Library 1.1.1
5718  * Copyright(c) 2006-2007, Ext JS, LLC.
5719  *
5720  * Originally Released Under LGPL - original licence link has changed is not relivant.
5721  *
5722  * Fork - LGPL
5723  * <script type="text/javascript">
5724  */
5725 /**
5726  * @class Roo.data.MemoryProxy
5727  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5728  * to the Reader when its load method is called.
5729  * @constructor
5730  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5731  */
5732 Roo.data.MemoryProxy = function(data){
5733     if (data.data) {
5734         data = data.data;
5735     }
5736     Roo.data.MemoryProxy.superclass.constructor.call(this);
5737     this.data = data;
5738 };
5739
5740 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5741     /**
5742      * Load data from the requested source (in this case an in-memory
5743      * data object passed to the constructor), read the data object into
5744      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5745      * process that block using the passed callback.
5746      * @param {Object} params This parameter is not used by the MemoryProxy class.
5747      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5748      * object into a block of Roo.data.Records.
5749      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5750      * The function must be passed <ul>
5751      * <li>The Record block object</li>
5752      * <li>The "arg" argument from the load function</li>
5753      * <li>A boolean success indicator</li>
5754      * </ul>
5755      * @param {Object} scope The scope in which to call the callback
5756      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5757      */
5758     load : function(params, reader, callback, scope, arg){
5759         params = params || {};
5760         var result;
5761         try {
5762             result = reader.readRecords(this.data);
5763         }catch(e){
5764             this.fireEvent("loadexception", this, arg, null, e);
5765             callback.call(scope, null, arg, false);
5766             return;
5767         }
5768         callback.call(scope, result, arg, true);
5769     },
5770     
5771     // private
5772     update : function(params, records){
5773         
5774     }
5775 });/*
5776  * Based on:
5777  * Ext JS Library 1.1.1
5778  * Copyright(c) 2006-2007, Ext JS, LLC.
5779  *
5780  * Originally Released Under LGPL - original licence link has changed is not relivant.
5781  *
5782  * Fork - LGPL
5783  * <script type="text/javascript">
5784  */
5785 /**
5786  * @class Roo.data.HttpProxy
5787  * @extends Roo.data.DataProxy
5788  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5789  * configured to reference a certain URL.<br><br>
5790  * <p>
5791  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5792  * from which the running page was served.<br><br>
5793  * <p>
5794  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5795  * <p>
5796  * Be aware that to enable the browser to parse an XML document, the server must set
5797  * the Content-Type header in the HTTP response to "text/xml".
5798  * @constructor
5799  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5800  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5801  * will be used to make the request.
5802  */
5803 Roo.data.HttpProxy = function(conn){
5804     Roo.data.HttpProxy.superclass.constructor.call(this);
5805     // is conn a conn config or a real conn?
5806     this.conn = conn;
5807     this.useAjax = !conn || !conn.events;
5808   
5809 };
5810
5811 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5812     // thse are take from connection...
5813     
5814     /**
5815      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5816      */
5817     /**
5818      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5819      * extra parameters to each request made by this object. (defaults to undefined)
5820      */
5821     /**
5822      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5823      *  to each request made by this object. (defaults to undefined)
5824      */
5825     /**
5826      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5827      */
5828     /**
5829      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5830      */
5831      /**
5832      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5833      * @type Boolean
5834      */
5835   
5836
5837     /**
5838      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5839      * @type Boolean
5840      */
5841     /**
5842      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5843      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5844      * a finer-grained basis than the DataProxy events.
5845      */
5846     getConnection : function(){
5847         return this.useAjax ? Roo.Ajax : this.conn;
5848     },
5849
5850     /**
5851      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5852      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5853      * process that block using the passed callback.
5854      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5855      * for the request to the remote server.
5856      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5857      * object into a block of Roo.data.Records.
5858      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5859      * The function must be passed <ul>
5860      * <li>The Record block object</li>
5861      * <li>The "arg" argument from the load function</li>
5862      * <li>A boolean success indicator</li>
5863      * </ul>
5864      * @param {Object} scope The scope in which to call the callback
5865      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5866      */
5867     load : function(params, reader, callback, scope, arg){
5868         if(this.fireEvent("beforeload", this, params) !== false){
5869             var  o = {
5870                 params : params || {},
5871                 request: {
5872                     callback : callback,
5873                     scope : scope,
5874                     arg : arg
5875                 },
5876                 reader: reader,
5877                 callback : this.loadResponse,
5878                 scope: this
5879             };
5880             if(this.useAjax){
5881                 Roo.applyIf(o, this.conn);
5882                 if(this.activeRequest){
5883                     Roo.Ajax.abort(this.activeRequest);
5884                 }
5885                 this.activeRequest = Roo.Ajax.request(o);
5886             }else{
5887                 this.conn.request(o);
5888             }
5889         }else{
5890             callback.call(scope||this, null, arg, false);
5891         }
5892     },
5893
5894     // private
5895     loadResponse : function(o, success, response){
5896         delete this.activeRequest;
5897         if(!success){
5898             this.fireEvent("loadexception", this, o, response);
5899             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5900             return;
5901         }
5902         var result;
5903         try {
5904             result = o.reader.read(response);
5905         }catch(e){
5906             this.fireEvent("loadexception", this, o, response, e);
5907             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5908             return;
5909         }
5910         
5911         this.fireEvent("load", this, o, o.request.arg);
5912         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5913     },
5914
5915     // private
5916     update : function(dataSet){
5917
5918     },
5919
5920     // private
5921     updateResponse : function(dataSet){
5922
5923     }
5924 });/*
5925  * Based on:
5926  * Ext JS Library 1.1.1
5927  * Copyright(c) 2006-2007, Ext JS, LLC.
5928  *
5929  * Originally Released Under LGPL - original licence link has changed is not relivant.
5930  *
5931  * Fork - LGPL
5932  * <script type="text/javascript">
5933  */
5934
5935 /**
5936  * @class Roo.data.ScriptTagProxy
5937  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5938  * other than the originating domain of the running page.<br><br>
5939  * <p>
5940  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5941  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5942  * <p>
5943  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5944  * source code that is used as the source inside a &lt;script> tag.<br><br>
5945  * <p>
5946  * In order for the browser to process the returned data, the server must wrap the data object
5947  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5948  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5949  * depending on whether the callback name was passed:
5950  * <p>
5951  * <pre><code>
5952 boolean scriptTag = false;
5953 String cb = request.getParameter("callback");
5954 if (cb != null) {
5955     scriptTag = true;
5956     response.setContentType("text/javascript");
5957 } else {
5958     response.setContentType("application/x-json");
5959 }
5960 Writer out = response.getWriter();
5961 if (scriptTag) {
5962     out.write(cb + "(");
5963 }
5964 out.print(dataBlock.toJsonString());
5965 if (scriptTag) {
5966     out.write(");");
5967 }
5968 </pre></code>
5969  *
5970  * @constructor
5971  * @param {Object} config A configuration object.
5972  */
5973 Roo.data.ScriptTagProxy = function(config){
5974     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5975     Roo.apply(this, config);
5976     this.head = document.getElementsByTagName("head")[0];
5977 };
5978
5979 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5980
5981 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5982     /**
5983      * @cfg {String} url The URL from which to request the data object.
5984      */
5985     /**
5986      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5987      */
5988     timeout : 30000,
5989     /**
5990      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5991      * the server the name of the callback function set up by the load call to process the returned data object.
5992      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5993      * javascript output which calls this named function passing the data object as its only parameter.
5994      */
5995     callbackParam : "callback",
5996     /**
5997      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5998      * name to the request.
5999      */
6000     nocache : true,
6001
6002     /**
6003      * Load data from the configured URL, read the data object into
6004      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6005      * process that block using the passed callback.
6006      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6007      * for the request to the remote server.
6008      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6009      * object into a block of Roo.data.Records.
6010      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6011      * The function must be passed <ul>
6012      * <li>The Record block object</li>
6013      * <li>The "arg" argument from the load function</li>
6014      * <li>A boolean success indicator</li>
6015      * </ul>
6016      * @param {Object} scope The scope in which to call the callback
6017      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6018      */
6019     load : function(params, reader, callback, scope, arg){
6020         if(this.fireEvent("beforeload", this, params) !== false){
6021
6022             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6023
6024             var url = this.url;
6025             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6026             if(this.nocache){
6027                 url += "&_dc=" + (new Date().getTime());
6028             }
6029             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6030             var trans = {
6031                 id : transId,
6032                 cb : "stcCallback"+transId,
6033                 scriptId : "stcScript"+transId,
6034                 params : params,
6035                 arg : arg,
6036                 url : url,
6037                 callback : callback,
6038                 scope : scope,
6039                 reader : reader
6040             };
6041             var conn = this;
6042
6043             window[trans.cb] = function(o){
6044                 conn.handleResponse(o, trans);
6045             };
6046
6047             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6048
6049             if(this.autoAbort !== false){
6050                 this.abort();
6051             }
6052
6053             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6054
6055             var script = document.createElement("script");
6056             script.setAttribute("src", url);
6057             script.setAttribute("type", "text/javascript");
6058             script.setAttribute("id", trans.scriptId);
6059             this.head.appendChild(script);
6060
6061             this.trans = trans;
6062         }else{
6063             callback.call(scope||this, null, arg, false);
6064         }
6065     },
6066
6067     // private
6068     isLoading : function(){
6069         return this.trans ? true : false;
6070     },
6071
6072     /**
6073      * Abort the current server request.
6074      */
6075     abort : function(){
6076         if(this.isLoading()){
6077             this.destroyTrans(this.trans);
6078         }
6079     },
6080
6081     // private
6082     destroyTrans : function(trans, isLoaded){
6083         this.head.removeChild(document.getElementById(trans.scriptId));
6084         clearTimeout(trans.timeoutId);
6085         if(isLoaded){
6086             window[trans.cb] = undefined;
6087             try{
6088                 delete window[trans.cb];
6089             }catch(e){}
6090         }else{
6091             // if hasn't been loaded, wait for load to remove it to prevent script error
6092             window[trans.cb] = function(){
6093                 window[trans.cb] = undefined;
6094                 try{
6095                     delete window[trans.cb];
6096                 }catch(e){}
6097             };
6098         }
6099     },
6100
6101     // private
6102     handleResponse : function(o, trans){
6103         this.trans = false;
6104         this.destroyTrans(trans, true);
6105         var result;
6106         try {
6107             result = trans.reader.readRecords(o);
6108         }catch(e){
6109             this.fireEvent("loadexception", this, o, trans.arg, e);
6110             trans.callback.call(trans.scope||window, null, trans.arg, false);
6111             return;
6112         }
6113         this.fireEvent("load", this, o, trans.arg);
6114         trans.callback.call(trans.scope||window, result, trans.arg, true);
6115     },
6116
6117     // private
6118     handleFailure : function(trans){
6119         this.trans = false;
6120         this.destroyTrans(trans, false);
6121         this.fireEvent("loadexception", this, null, trans.arg);
6122         trans.callback.call(trans.scope||window, null, trans.arg, false);
6123     }
6124 });/*
6125  * Based on:
6126  * Ext JS Library 1.1.1
6127  * Copyright(c) 2006-2007, Ext JS, LLC.
6128  *
6129  * Originally Released Under LGPL - original licence link has changed is not relivant.
6130  *
6131  * Fork - LGPL
6132  * <script type="text/javascript">
6133  */
6134
6135 /**
6136  * @class Roo.data.JsonReader
6137  * @extends Roo.data.DataReader
6138  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6139  * based on mappings in a provided Roo.data.Record constructor.
6140  * 
6141  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6142  * in the reply previously. 
6143  * 
6144  * <p>
6145  * Example code:
6146  * <pre><code>
6147 var RecordDef = Roo.data.Record.create([
6148     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6149     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6150 ]);
6151 var myReader = new Roo.data.JsonReader({
6152     totalProperty: "results",    // The property which contains the total dataset size (optional)
6153     root: "rows",                // The property which contains an Array of row objects
6154     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6155 }, RecordDef);
6156 </code></pre>
6157  * <p>
6158  * This would consume a JSON file like this:
6159  * <pre><code>
6160 { 'results': 2, 'rows': [
6161     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6162     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6163 }
6164 </code></pre>
6165  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6166  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6167  * paged from the remote server.
6168  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6169  * @cfg {String} root name of the property which contains the Array of row objects.
6170  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6171  * @constructor
6172  * Create a new JsonReader
6173  * @param {Object} meta Metadata configuration options
6174  * @param {Object} recordType Either an Array of field definition objects,
6175  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6176  */
6177 Roo.data.JsonReader = function(meta, recordType){
6178     
6179     meta = meta || {};
6180     // set some defaults:
6181     Roo.applyIf(meta, {
6182         totalProperty: 'total',
6183         successProperty : 'success',
6184         root : 'data',
6185         id : 'id'
6186     });
6187     
6188     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6189 };
6190 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6191     
6192     /**
6193      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6194      * Used by Store query builder to append _requestMeta to params.
6195      * 
6196      */
6197     metaFromRemote : false,
6198     /**
6199      * This method is only used by a DataProxy which has retrieved data from a remote server.
6200      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6201      * @return {Object} data A data block which is used by an Roo.data.Store object as
6202      * a cache of Roo.data.Records.
6203      */
6204     read : function(response){
6205         var json = response.responseText;
6206        
6207         var o = /* eval:var:o */ eval("("+json+")");
6208         if(!o) {
6209             throw {message: "JsonReader.read: Json object not found"};
6210         }
6211         
6212         if(o.metaData){
6213             
6214             delete this.ef;
6215             this.metaFromRemote = true;
6216             this.meta = o.metaData;
6217             this.recordType = Roo.data.Record.create(o.metaData.fields);
6218             this.onMetaChange(this.meta, this.recordType, o);
6219         }
6220         return this.readRecords(o);
6221     },
6222
6223     // private function a store will implement
6224     onMetaChange : function(meta, recordType, o){
6225
6226     },
6227
6228     /**
6229          * @ignore
6230          */
6231     simpleAccess: function(obj, subsc) {
6232         return obj[subsc];
6233     },
6234
6235         /**
6236          * @ignore
6237          */
6238     getJsonAccessor: function(){
6239         var re = /[\[\.]/;
6240         return function(expr) {
6241             try {
6242                 return(re.test(expr))
6243                     ? new Function("obj", "return obj." + expr)
6244                     : function(obj){
6245                         return obj[expr];
6246                     };
6247             } catch(e){}
6248             return Roo.emptyFn;
6249         };
6250     }(),
6251
6252     /**
6253      * Create a data block containing Roo.data.Records from an XML document.
6254      * @param {Object} o An object which contains an Array of row objects in the property specified
6255      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6256      * which contains the total size of the dataset.
6257      * @return {Object} data A data block which is used by an Roo.data.Store object as
6258      * a cache of Roo.data.Records.
6259      */
6260     readRecords : function(o){
6261         /**
6262          * After any data loads, the raw JSON data is available for further custom processing.
6263          * @type Object
6264          */
6265         this.o = o;
6266         var s = this.meta, Record = this.recordType,
6267             f = Record.prototype.fields, fi = f.items, fl = f.length;
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     if (typeof(depreciated_tpl) == 'undefined') {
8432         // new way.. - universal constructor.
8433         Roo.apply(this, config);
8434         this.el  = Roo.get(this.el);
8435     } else {
8436         // old format..
8437         this.el  = Roo.get(config);
8438         this.tpl = depreciated_tpl;
8439         Roo.apply(this, depreciated_config);
8440     }
8441     this.wrapEl  = this.el.wrap().wrap();
8442     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8443     
8444     
8445     if(typeof(this.tpl) == "string"){
8446         this.tpl = new Roo.Template(this.tpl);
8447     } else {
8448         // support xtype ctors..
8449         this.tpl = new Roo.factory(this.tpl, Roo);
8450     }
8451     
8452     
8453     this.tpl.compile();
8454    
8455   
8456     
8457      
8458     /** @private */
8459     this.addEvents({
8460         /**
8461          * @event beforeclick
8462          * Fires before a click is processed. Returns false to cancel the default action.
8463          * @param {Roo.View} this
8464          * @param {Number} index The index of the target node
8465          * @param {HTMLElement} node The target node
8466          * @param {Roo.EventObject} e The raw event object
8467          */
8468             "beforeclick" : true,
8469         /**
8470          * @event click
8471          * Fires when a template node is clicked.
8472          * @param {Roo.View} this
8473          * @param {Number} index The index of the target node
8474          * @param {HTMLElement} node The target node
8475          * @param {Roo.EventObject} e The raw event object
8476          */
8477             "click" : true,
8478         /**
8479          * @event dblclick
8480          * Fires when a template node is double clicked.
8481          * @param {Roo.View} this
8482          * @param {Number} index The index of the target node
8483          * @param {HTMLElement} node The target node
8484          * @param {Roo.EventObject} e The raw event object
8485          */
8486             "dblclick" : true,
8487         /**
8488          * @event contextmenu
8489          * Fires when a template node is right clicked.
8490          * @param {Roo.View} this
8491          * @param {Number} index The index of the target node
8492          * @param {HTMLElement} node The target node
8493          * @param {Roo.EventObject} e The raw event object
8494          */
8495             "contextmenu" : true,
8496         /**
8497          * @event selectionchange
8498          * Fires when the selected nodes change.
8499          * @param {Roo.View} this
8500          * @param {Array} selections Array of the selected nodes
8501          */
8502             "selectionchange" : true,
8503     
8504         /**
8505          * @event beforeselect
8506          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8507          * @param {Roo.View} this
8508          * @param {HTMLElement} node The node to be selected
8509          * @param {Array} selections Array of currently selected nodes
8510          */
8511             "beforeselect" : true,
8512         /**
8513          * @event preparedata
8514          * Fires on every row to render, to allow you to change the data.
8515          * @param {Roo.View} this
8516          * @param {Object} data to be rendered (change this)
8517          */
8518           "preparedata" : true
8519           
8520           
8521         });
8522
8523
8524
8525     this.el.on({
8526         "click": this.onClick,
8527         "dblclick": this.onDblClick,
8528         "contextmenu": this.onContextMenu,
8529         scope:this
8530     });
8531
8532     this.selections = [];
8533     this.nodes = [];
8534     this.cmp = new Roo.CompositeElementLite([]);
8535     if(this.store){
8536         this.store = Roo.factory(this.store, Roo.data);
8537         this.setStore(this.store, true);
8538     }
8539     
8540     if ( this.footer && this.footer.xtype) {
8541            
8542          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8543         
8544         this.footer.dataSource = this.store
8545         this.footer.container = fctr;
8546         this.footer = Roo.factory(this.footer, Roo);
8547         fctr.insertFirst(this.el);
8548         
8549         // this is a bit insane - as the paging toolbar seems to detach the el..
8550 //        dom.parentNode.parentNode.parentNode
8551          // they get detached?
8552     }
8553     
8554     
8555     Roo.View.superclass.constructor.call(this);
8556     
8557     
8558 };
8559
8560 Roo.extend(Roo.View, Roo.util.Observable, {
8561     
8562      /**
8563      * @cfg {Roo.data.Store} store Data store to load data from.
8564      */
8565     store : false,
8566     
8567     /**
8568      * @cfg {String|Roo.Element} el The container element.
8569      */
8570     el : '',
8571     
8572     /**
8573      * @cfg {String|Roo.Template} tpl The template used by this View 
8574      */
8575     tpl : false,
8576     /**
8577      * @cfg {String} dataName the named area of the template to use as the data area
8578      *                          Works with domtemplates roo-name="name"
8579      */
8580     dataName: false,
8581     /**
8582      * @cfg {String} selectedClass The css class to add to selected nodes
8583      */
8584     selectedClass : "x-view-selected",
8585      /**
8586      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8587      */
8588     emptyText : "",
8589     
8590     /**
8591      * @cfg {String} text to display on mask (default Loading)
8592      */
8593     mask : false,
8594     /**
8595      * @cfg {Boolean} multiSelect Allow multiple selection
8596      */
8597     multiSelect : false,
8598     /**
8599      * @cfg {Boolean} singleSelect Allow single selection
8600      */
8601     singleSelect:  false,
8602     
8603     /**
8604      * @cfg {Boolean} toggleSelect - selecting 
8605      */
8606     toggleSelect : false,
8607     
8608     /**
8609      * Returns the element this view is bound to.
8610      * @return {Roo.Element}
8611      */
8612     getEl : function(){
8613         return this.wrapEl;
8614     },
8615     
8616     
8617
8618     /**
8619      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8620      */
8621     refresh : function(){
8622         Roo.log('refresh');
8623         var t = this.tpl;
8624         
8625         // if we are using something like 'domtemplate', then
8626         // the what gets used is:
8627         // t.applySubtemplate(NAME, data, wrapping data..)
8628         // the outer template then get' applied with
8629         //     the store 'extra data'
8630         // and the body get's added to the
8631         //      roo-name="data" node?
8632         //      <span class='roo-tpl-{name}'></span> ?????
8633         
8634         
8635         
8636         this.clearSelections();
8637         this.el.update("");
8638         var html = [];
8639         var records = this.store.getRange();
8640         if(records.length < 1) {
8641             
8642             // is this valid??  = should it render a template??
8643             
8644             this.el.update(this.emptyText);
8645             return;
8646         }
8647         var el = this.el;
8648         if (this.dataName) {
8649             this.el.update(t.apply(this.store.meta)); //????
8650             el = this.el.child('.roo-tpl-' + this.dataName);
8651         }
8652         
8653         for(var i = 0, len = records.length; i < len; i++){
8654             var data = this.prepareData(records[i].data, i, records[i]);
8655             this.fireEvent("preparedata", this, data, i, records[i]);
8656             html[html.length] = Roo.util.Format.trim(
8657                 this.dataName ?
8658                     t.applySubtemplate(this.dataName, data, this.store.meta) :
8659                     t.apply(data)
8660             );
8661         }
8662         
8663         
8664         
8665         el.update(html.join(""));
8666         this.nodes = el.dom.childNodes;
8667         this.updateIndexes(0);
8668     },
8669     
8670
8671     /**
8672      * Function to override to reformat the data that is sent to
8673      * the template for each node.
8674      * DEPRICATED - use the preparedata event handler.
8675      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8676      * a JSON object for an UpdateManager bound view).
8677      */
8678     prepareData : function(data, index, record)
8679     {
8680         this.fireEvent("preparedata", this, data, index, record);
8681         return data;
8682     },
8683
8684     onUpdate : function(ds, record){
8685          Roo.log('on update');   
8686         this.clearSelections();
8687         var index = this.store.indexOf(record);
8688         var n = this.nodes[index];
8689         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8690         n.parentNode.removeChild(n);
8691         this.updateIndexes(index, index);
8692     },
8693
8694     
8695     
8696 // --------- FIXME     
8697     onAdd : function(ds, records, index)
8698     {
8699         Roo.log(['on Add', ds, records, index] );        
8700         this.clearSelections();
8701         if(this.nodes.length == 0){
8702             this.refresh();
8703             return;
8704         }
8705         var n = this.nodes[index];
8706         for(var i = 0, len = records.length; i < len; i++){
8707             var d = this.prepareData(records[i].data, i, records[i]);
8708             if(n){
8709                 this.tpl.insertBefore(n, d);
8710             }else{
8711                 
8712                 this.tpl.append(this.el, d);
8713             }
8714         }
8715         this.updateIndexes(index);
8716     },
8717
8718     onRemove : function(ds, record, index){
8719         Roo.log('onRemove');
8720         this.clearSelections();
8721         var el = this.dataName  ?
8722             this.el.child('.roo-tpl-' + this.dataName) :
8723             this.el; 
8724         
8725         el.dom.removeChild(this.nodes[index]);
8726         this.updateIndexes(index);
8727     },
8728
8729     /**
8730      * Refresh an individual node.
8731      * @param {Number} index
8732      */
8733     refreshNode : function(index){
8734         this.onUpdate(this.store, this.store.getAt(index));
8735     },
8736
8737     updateIndexes : function(startIndex, endIndex){
8738         var ns = this.nodes;
8739         startIndex = startIndex || 0;
8740         endIndex = endIndex || ns.length - 1;
8741         for(var i = startIndex; i <= endIndex; i++){
8742             ns[i].nodeIndex = i;
8743         }
8744     },
8745
8746     /**
8747      * Changes the data store this view uses and refresh the view.
8748      * @param {Store} store
8749      */
8750     setStore : function(store, initial){
8751         if(!initial && this.store){
8752             this.store.un("datachanged", this.refresh);
8753             this.store.un("add", this.onAdd);
8754             this.store.un("remove", this.onRemove);
8755             this.store.un("update", this.onUpdate);
8756             this.store.un("clear", this.refresh);
8757             this.store.un("beforeload", this.onBeforeLoad);
8758             this.store.un("load", this.onLoad);
8759             this.store.un("loadexception", this.onLoad);
8760         }
8761         if(store){
8762           
8763             store.on("datachanged", this.refresh, this);
8764             store.on("add", this.onAdd, this);
8765             store.on("remove", this.onRemove, this);
8766             store.on("update", this.onUpdate, this);
8767             store.on("clear", this.refresh, this);
8768             store.on("beforeload", this.onBeforeLoad, this);
8769             store.on("load", this.onLoad, this);
8770             store.on("loadexception", this.onLoad, this);
8771         }
8772         
8773         if(store){
8774             this.refresh();
8775         }
8776     },
8777     /**
8778      * onbeforeLoad - masks the loading area.
8779      *
8780      */
8781     onBeforeLoad : function(store,opts)
8782     {
8783          Roo.log('onBeforeLoad');   
8784         if (!opts.add) {
8785             this.el.update("");
8786         }
8787         this.el.mask(this.mask ? this.mask : "Loading" ); 
8788     },
8789     onLoad : function ()
8790     {
8791         this.el.unmask();
8792     },
8793     
8794
8795     /**
8796      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8797      * @param {HTMLElement} node
8798      * @return {HTMLElement} The template node
8799      */
8800     findItemFromChild : function(node){
8801         var el = this.dataName  ?
8802             this.el.child('.roo-tpl-' + this.dataName,true) :
8803             this.el.dom; 
8804         
8805         if(!node || node.parentNode == el){
8806                     return node;
8807             }
8808             var p = node.parentNode;
8809             while(p && p != el){
8810             if(p.parentNode == el){
8811                 return p;
8812             }
8813             p = p.parentNode;
8814         }
8815             return null;
8816     },
8817
8818     /** @ignore */
8819     onClick : function(e){
8820         var item = this.findItemFromChild(e.getTarget());
8821         if(item){
8822             var index = this.indexOf(item);
8823             if(this.onItemClick(item, index, e) !== false){
8824                 this.fireEvent("click", this, index, item, e);
8825             }
8826         }else{
8827             this.clearSelections();
8828         }
8829     },
8830
8831     /** @ignore */
8832     onContextMenu : function(e){
8833         var item = this.findItemFromChild(e.getTarget());
8834         if(item){
8835             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8836         }
8837     },
8838
8839     /** @ignore */
8840     onDblClick : function(e){
8841         var item = this.findItemFromChild(e.getTarget());
8842         if(item){
8843             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8844         }
8845     },
8846
8847     onItemClick : function(item, index, e)
8848     {
8849         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8850             return false;
8851         }
8852         if (this.toggleSelect) {
8853             var m = this.isSelected(item) ? 'unselect' : 'select';
8854             Roo.log(m);
8855             var _t = this;
8856             _t[m](item, true, false);
8857             return true;
8858         }
8859         if(this.multiSelect || this.singleSelect){
8860             if(this.multiSelect && e.shiftKey && this.lastSelection){
8861                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8862             }else{
8863                 this.select(item, this.multiSelect && e.ctrlKey);
8864                 this.lastSelection = item;
8865             }
8866             e.preventDefault();
8867         }
8868         return true;
8869     },
8870
8871     /**
8872      * Get the number of selected nodes.
8873      * @return {Number}
8874      */
8875     getSelectionCount : function(){
8876         return this.selections.length;
8877     },
8878
8879     /**
8880      * Get the currently selected nodes.
8881      * @return {Array} An array of HTMLElements
8882      */
8883     getSelectedNodes : function(){
8884         return this.selections;
8885     },
8886
8887     /**
8888      * Get the indexes of the selected nodes.
8889      * @return {Array}
8890      */
8891     getSelectedIndexes : function(){
8892         var indexes = [], s = this.selections;
8893         for(var i = 0, len = s.length; i < len; i++){
8894             indexes.push(s[i].nodeIndex);
8895         }
8896         return indexes;
8897     },
8898
8899     /**
8900      * Clear all selections
8901      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8902      */
8903     clearSelections : function(suppressEvent){
8904         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8905             this.cmp.elements = this.selections;
8906             this.cmp.removeClass(this.selectedClass);
8907             this.selections = [];
8908             if(!suppressEvent){
8909                 this.fireEvent("selectionchange", this, this.selections);
8910             }
8911         }
8912     },
8913
8914     /**
8915      * Returns true if the passed node is selected
8916      * @param {HTMLElement/Number} node The node or node index
8917      * @return {Boolean}
8918      */
8919     isSelected : function(node){
8920         var s = this.selections;
8921         if(s.length < 1){
8922             return false;
8923         }
8924         node = this.getNode(node);
8925         return s.indexOf(node) !== -1;
8926     },
8927
8928     /**
8929      * Selects nodes.
8930      * @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
8931      * @param {Boolean} keepExisting (optional) true to keep existing selections
8932      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8933      */
8934     select : function(nodeInfo, keepExisting, suppressEvent){
8935         if(nodeInfo instanceof Array){
8936             if(!keepExisting){
8937                 this.clearSelections(true);
8938             }
8939             for(var i = 0, len = nodeInfo.length; i < len; i++){
8940                 this.select(nodeInfo[i], true, true);
8941             }
8942             return;
8943         } 
8944         var node = this.getNode(nodeInfo);
8945         if(!node || this.isSelected(node)){
8946             return; // already selected.
8947         }
8948         if(!keepExisting){
8949             this.clearSelections(true);
8950         }
8951         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8952             Roo.fly(node).addClass(this.selectedClass);
8953             this.selections.push(node);
8954             if(!suppressEvent){
8955                 this.fireEvent("selectionchange", this, this.selections);
8956             }
8957         }
8958         
8959         
8960     },
8961       /**
8962      * Unselects nodes.
8963      * @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
8964      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8965      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8966      */
8967     unselect : function(nodeInfo, keepExisting, suppressEvent)
8968     {
8969         if(nodeInfo instanceof Array){
8970             Roo.each(this.selections, function(s) {
8971                 this.unselect(s, nodeInfo);
8972             }, this);
8973             return;
8974         }
8975         var node = this.getNode(nodeInfo);
8976         if(!node || !this.isSelected(node)){
8977             Roo.log("not selected");
8978             return; // not selected.
8979         }
8980         // fireevent???
8981         var ns = [];
8982         Roo.each(this.selections, function(s) {
8983             if (s == node ) {
8984                 Roo.fly(node).removeClass(this.selectedClass);
8985
8986                 return;
8987             }
8988             ns.push(s);
8989         },this);
8990         
8991         this.selections= ns;
8992         this.fireEvent("selectionchange", this, this.selections);
8993     },
8994
8995     /**
8996      * Gets a template node.
8997      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
8998      * @return {HTMLElement} The node or null if it wasn't found
8999      */
9000     getNode : function(nodeInfo){
9001         if(typeof nodeInfo == "string"){
9002             return document.getElementById(nodeInfo);
9003         }else if(typeof nodeInfo == "number"){
9004             return this.nodes[nodeInfo];
9005         }
9006         return nodeInfo;
9007     },
9008
9009     /**
9010      * Gets a range template nodes.
9011      * @param {Number} startIndex
9012      * @param {Number} endIndex
9013      * @return {Array} An array of nodes
9014      */
9015     getNodes : function(start, end){
9016         var ns = this.nodes;
9017         start = start || 0;
9018         end = typeof end == "undefined" ? ns.length - 1 : end;
9019         var nodes = [];
9020         if(start <= end){
9021             for(var i = start; i <= end; i++){
9022                 nodes.push(ns[i]);
9023             }
9024         } else{
9025             for(var i = start; i >= end; i--){
9026                 nodes.push(ns[i]);
9027             }
9028         }
9029         return nodes;
9030     },
9031
9032     /**
9033      * Finds the index of the passed node
9034      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9035      * @return {Number} The index of the node or -1
9036      */
9037     indexOf : function(node){
9038         node = this.getNode(node);
9039         if(typeof node.nodeIndex == "number"){
9040             return node.nodeIndex;
9041         }
9042         var ns = this.nodes;
9043         for(var i = 0, len = ns.length; i < len; i++){
9044             if(ns[i] == node){
9045                 return i;
9046             }
9047         }
9048         return -1;
9049     }
9050 });
9051 /*
9052  * Based on:
9053  * Ext JS Library 1.1.1
9054  * Copyright(c) 2006-2007, Ext JS, LLC.
9055  *
9056  * Originally Released Under LGPL - original licence link has changed is not relivant.
9057  *
9058  * Fork - LGPL
9059  * <script type="text/javascript">
9060  */
9061
9062 /**
9063  * @class Roo.JsonView
9064  * @extends Roo.View
9065  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9066 <pre><code>
9067 var view = new Roo.JsonView({
9068     container: "my-element",
9069     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9070     multiSelect: true, 
9071     jsonRoot: "data" 
9072 });
9073
9074 // listen for node click?
9075 view.on("click", function(vw, index, node, e){
9076     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9077 });
9078
9079 // direct load of JSON data
9080 view.load("foobar.php");
9081
9082 // Example from my blog list
9083 var tpl = new Roo.Template(
9084     '&lt;div class="entry"&gt;' +
9085     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9086     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9087     "&lt;/div&gt;&lt;hr /&gt;"
9088 );
9089
9090 var moreView = new Roo.JsonView({
9091     container :  "entry-list", 
9092     template : tpl,
9093     jsonRoot: "posts"
9094 });
9095 moreView.on("beforerender", this.sortEntries, this);
9096 moreView.load({
9097     url: "/blog/get-posts.php",
9098     params: "allposts=true",
9099     text: "Loading Blog Entries..."
9100 });
9101 </code></pre>
9102
9103 * Note: old code is supported with arguments : (container, template, config)
9104
9105
9106  * @constructor
9107  * Create a new JsonView
9108  * 
9109  * @param {Object} config The config object
9110  * 
9111  */
9112 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9113     
9114     
9115     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9116
9117     var um = this.el.getUpdateManager();
9118     um.setRenderer(this);
9119     um.on("update", this.onLoad, this);
9120     um.on("failure", this.onLoadException, this);
9121
9122     /**
9123      * @event beforerender
9124      * Fires before rendering of the downloaded JSON data.
9125      * @param {Roo.JsonView} this
9126      * @param {Object} data The JSON data loaded
9127      */
9128     /**
9129      * @event load
9130      * Fires when data is loaded.
9131      * @param {Roo.JsonView} this
9132      * @param {Object} data The JSON data loaded
9133      * @param {Object} response The raw Connect response object
9134      */
9135     /**
9136      * @event loadexception
9137      * Fires when loading fails.
9138      * @param {Roo.JsonView} this
9139      * @param {Object} response The raw Connect response object
9140      */
9141     this.addEvents({
9142         'beforerender' : true,
9143         'load' : true,
9144         'loadexception' : true
9145     });
9146 };
9147 Roo.extend(Roo.JsonView, Roo.View, {
9148     /**
9149      * @type {String} The root property in the loaded JSON object that contains the data
9150      */
9151     jsonRoot : "",
9152
9153     /**
9154      * Refreshes the view.
9155      */
9156     refresh : function(){
9157         this.clearSelections();
9158         this.el.update("");
9159         var html = [];
9160         var o = this.jsonData;
9161         if(o && o.length > 0){
9162             for(var i = 0, len = o.length; i < len; i++){
9163                 var data = this.prepareData(o[i], i, o);
9164                 html[html.length] = this.tpl.apply(data);
9165             }
9166         }else{
9167             html.push(this.emptyText);
9168         }
9169         this.el.update(html.join(""));
9170         this.nodes = this.el.dom.childNodes;
9171         this.updateIndexes(0);
9172     },
9173
9174     /**
9175      * 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.
9176      * @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:
9177      <pre><code>
9178      view.load({
9179          url: "your-url.php",
9180          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9181          callback: yourFunction,
9182          scope: yourObject, //(optional scope)
9183          discardUrl: false,
9184          nocache: false,
9185          text: "Loading...",
9186          timeout: 30,
9187          scripts: false
9188      });
9189      </code></pre>
9190      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9191      * 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.
9192      * @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}
9193      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9194      * @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.
9195      */
9196     load : function(){
9197         var um = this.el.getUpdateManager();
9198         um.update.apply(um, arguments);
9199     },
9200
9201     render : function(el, response){
9202         this.clearSelections();
9203         this.el.update("");
9204         var o;
9205         try{
9206             o = Roo.util.JSON.decode(response.responseText);
9207             if(this.jsonRoot){
9208                 
9209                 o = o[this.jsonRoot];
9210             }
9211         } catch(e){
9212         }
9213         /**
9214          * The current JSON data or null
9215          */
9216         this.jsonData = o;
9217         this.beforeRender();
9218         this.refresh();
9219     },
9220
9221 /**
9222  * Get the number of records in the current JSON dataset
9223  * @return {Number}
9224  */
9225     getCount : function(){
9226         return this.jsonData ? this.jsonData.length : 0;
9227     },
9228
9229 /**
9230  * Returns the JSON object for the specified node(s)
9231  * @param {HTMLElement/Array} node The node or an array of nodes
9232  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9233  * you get the JSON object for the node
9234  */
9235     getNodeData : function(node){
9236         if(node instanceof Array){
9237             var data = [];
9238             for(var i = 0, len = node.length; i < len; i++){
9239                 data.push(this.getNodeData(node[i]));
9240             }
9241             return data;
9242         }
9243         return this.jsonData[this.indexOf(node)] || null;
9244     },
9245
9246     beforeRender : function(){
9247         this.snapshot = this.jsonData;
9248         if(this.sortInfo){
9249             this.sort.apply(this, this.sortInfo);
9250         }
9251         this.fireEvent("beforerender", this, this.jsonData);
9252     },
9253
9254     onLoad : function(el, o){
9255         this.fireEvent("load", this, this.jsonData, o);
9256     },
9257
9258     onLoadException : function(el, o){
9259         this.fireEvent("loadexception", this, o);
9260     },
9261
9262 /**
9263  * Filter the data by a specific property.
9264  * @param {String} property A property on your JSON objects
9265  * @param {String/RegExp} value Either string that the property values
9266  * should start with, or a RegExp to test against the property
9267  */
9268     filter : function(property, value){
9269         if(this.jsonData){
9270             var data = [];
9271             var ss = this.snapshot;
9272             if(typeof value == "string"){
9273                 var vlen = value.length;
9274                 if(vlen == 0){
9275                     this.clearFilter();
9276                     return;
9277                 }
9278                 value = value.toLowerCase();
9279                 for(var i = 0, len = ss.length; i < len; i++){
9280                     var o = ss[i];
9281                     if(o[property].substr(0, vlen).toLowerCase() == value){
9282                         data.push(o);
9283                     }
9284                 }
9285             } else if(value.exec){ // regex?
9286                 for(var i = 0, len = ss.length; i < len; i++){
9287                     var o = ss[i];
9288                     if(value.test(o[property])){
9289                         data.push(o);
9290                     }
9291                 }
9292             } else{
9293                 return;
9294             }
9295             this.jsonData = data;
9296             this.refresh();
9297         }
9298     },
9299
9300 /**
9301  * Filter by a function. The passed function will be called with each
9302  * object in the current dataset. If the function returns true the value is kept,
9303  * otherwise it is filtered.
9304  * @param {Function} fn
9305  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9306  */
9307     filterBy : function(fn, scope){
9308         if(this.jsonData){
9309             var data = [];
9310             var ss = this.snapshot;
9311             for(var i = 0, len = ss.length; i < len; i++){
9312                 var o = ss[i];
9313                 if(fn.call(scope || this, o)){
9314                     data.push(o);
9315                 }
9316             }
9317             this.jsonData = data;
9318             this.refresh();
9319         }
9320     },
9321
9322 /**
9323  * Clears the current filter.
9324  */
9325     clearFilter : function(){
9326         if(this.snapshot && this.jsonData != this.snapshot){
9327             this.jsonData = this.snapshot;
9328             this.refresh();
9329         }
9330     },
9331
9332
9333 /**
9334  * Sorts the data for this view and refreshes it.
9335  * @param {String} property A property on your JSON objects to sort on
9336  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9337  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9338  */
9339     sort : function(property, dir, sortType){
9340         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9341         if(this.jsonData){
9342             var p = property;
9343             var dsc = dir && dir.toLowerCase() == "desc";
9344             var f = function(o1, o2){
9345                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9346                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9347                 ;
9348                 if(v1 < v2){
9349                     return dsc ? +1 : -1;
9350                 } else if(v1 > v2){
9351                     return dsc ? -1 : +1;
9352                 } else{
9353                     return 0;
9354                 }
9355             };
9356             this.jsonData.sort(f);
9357             this.refresh();
9358             if(this.jsonData != this.snapshot){
9359                 this.snapshot.sort(f);
9360             }
9361         }
9362     }
9363 });/*
9364  * Based on:
9365  * Ext JS Library 1.1.1
9366  * Copyright(c) 2006-2007, Ext JS, LLC.
9367  *
9368  * Originally Released Under LGPL - original licence link has changed is not relivant.
9369  *
9370  * Fork - LGPL
9371  * <script type="text/javascript">
9372  */
9373  
9374
9375 /**
9376  * @class Roo.ColorPalette
9377  * @extends Roo.Component
9378  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9379  * Here's an example of typical usage:
9380  * <pre><code>
9381 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9382 cp.render('my-div');
9383
9384 cp.on('select', function(palette, selColor){
9385     // do something with selColor
9386 });
9387 </code></pre>
9388  * @constructor
9389  * Create a new ColorPalette
9390  * @param {Object} config The config object
9391  */
9392 Roo.ColorPalette = function(config){
9393     Roo.ColorPalette.superclass.constructor.call(this, config);
9394     this.addEvents({
9395         /**
9396              * @event select
9397              * Fires when a color is selected
9398              * @param {ColorPalette} this
9399              * @param {String} color The 6-digit color hex code (without the # symbol)
9400              */
9401         select: true
9402     });
9403
9404     if(this.handler){
9405         this.on("select", this.handler, this.scope, true);
9406     }
9407 };
9408 Roo.extend(Roo.ColorPalette, Roo.Component, {
9409     /**
9410      * @cfg {String} itemCls
9411      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9412      */
9413     itemCls : "x-color-palette",
9414     /**
9415      * @cfg {String} value
9416      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9417      * the hex codes are case-sensitive.
9418      */
9419     value : null,
9420     clickEvent:'click',
9421     // private
9422     ctype: "Roo.ColorPalette",
9423
9424     /**
9425      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9426      */
9427     allowReselect : false,
9428
9429     /**
9430      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9431      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9432      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9433      * of colors with the width setting until the box is symmetrical.</p>
9434      * <p>You can override individual colors if needed:</p>
9435      * <pre><code>
9436 var cp = new Roo.ColorPalette();
9437 cp.colors[0] = "FF0000";  // change the first box to red
9438 </code></pre>
9439
9440 Or you can provide a custom array of your own for complete control:
9441 <pre><code>
9442 var cp = new Roo.ColorPalette();
9443 cp.colors = ["000000", "993300", "333300"];
9444 </code></pre>
9445      * @type Array
9446      */
9447     colors : [
9448         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9449         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9450         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9451         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9452         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9453     ],
9454
9455     // private
9456     onRender : function(container, position){
9457         var t = new Roo.MasterTemplate(
9458             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9459         );
9460         var c = this.colors;
9461         for(var i = 0, len = c.length; i < len; i++){
9462             t.add([c[i]]);
9463         }
9464         var el = document.createElement("div");
9465         el.className = this.itemCls;
9466         t.overwrite(el);
9467         container.dom.insertBefore(el, position);
9468         this.el = Roo.get(el);
9469         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9470         if(this.clickEvent != 'click'){
9471             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9472         }
9473     },
9474
9475     // private
9476     afterRender : function(){
9477         Roo.ColorPalette.superclass.afterRender.call(this);
9478         if(this.value){
9479             var s = this.value;
9480             this.value = null;
9481             this.select(s);
9482         }
9483     },
9484
9485     // private
9486     handleClick : function(e, t){
9487         e.preventDefault();
9488         if(!this.disabled){
9489             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9490             this.select(c.toUpperCase());
9491         }
9492     },
9493
9494     /**
9495      * Selects the specified color in the palette (fires the select event)
9496      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9497      */
9498     select : function(color){
9499         color = color.replace("#", "");
9500         if(color != this.value || this.allowReselect){
9501             var el = this.el;
9502             if(this.value){
9503                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9504             }
9505             el.child("a.color-"+color).addClass("x-color-palette-sel");
9506             this.value = color;
9507             this.fireEvent("select", this, color);
9508         }
9509     }
9510 });/*
9511  * Based on:
9512  * Ext JS Library 1.1.1
9513  * Copyright(c) 2006-2007, Ext JS, LLC.
9514  *
9515  * Originally Released Under LGPL - original licence link has changed is not relivant.
9516  *
9517  * Fork - LGPL
9518  * <script type="text/javascript">
9519  */
9520  
9521 /**
9522  * @class Roo.DatePicker
9523  * @extends Roo.Component
9524  * Simple date picker class.
9525  * @constructor
9526  * Create a new DatePicker
9527  * @param {Object} config The config object
9528  */
9529 Roo.DatePicker = function(config){
9530     Roo.DatePicker.superclass.constructor.call(this, config);
9531
9532     this.value = config && config.value ?
9533                  config.value.clearTime() : new Date().clearTime();
9534
9535     this.addEvents({
9536         /**
9537              * @event select
9538              * Fires when a date is selected
9539              * @param {DatePicker} this
9540              * @param {Date} date The selected date
9541              */
9542         'select': true,
9543         /**
9544              * @event monthchange
9545              * Fires when the displayed month changes 
9546              * @param {DatePicker} this
9547              * @param {Date} date The selected month
9548              */
9549         'monthchange': true
9550     });
9551
9552     if(this.handler){
9553         this.on("select", this.handler,  this.scope || this);
9554     }
9555     // build the disabledDatesRE
9556     if(!this.disabledDatesRE && this.disabledDates){
9557         var dd = this.disabledDates;
9558         var re = "(?:";
9559         for(var i = 0; i < dd.length; i++){
9560             re += dd[i];
9561             if(i != dd.length-1) re += "|";
9562         }
9563         this.disabledDatesRE = new RegExp(re + ")");
9564     }
9565 };
9566
9567 Roo.extend(Roo.DatePicker, Roo.Component, {
9568     /**
9569      * @cfg {String} todayText
9570      * The text to display on the button that selects the current date (defaults to "Today")
9571      */
9572     todayText : "Today",
9573     /**
9574      * @cfg {String} okText
9575      * The text to display on the ok button
9576      */
9577     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9578     /**
9579      * @cfg {String} cancelText
9580      * The text to display on the cancel button
9581      */
9582     cancelText : "Cancel",
9583     /**
9584      * @cfg {String} todayTip
9585      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9586      */
9587     todayTip : "{0} (Spacebar)",
9588     /**
9589      * @cfg {Date} minDate
9590      * Minimum allowable date (JavaScript date object, defaults to null)
9591      */
9592     minDate : null,
9593     /**
9594      * @cfg {Date} maxDate
9595      * Maximum allowable date (JavaScript date object, defaults to null)
9596      */
9597     maxDate : null,
9598     /**
9599      * @cfg {String} minText
9600      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9601      */
9602     minText : "This date is before the minimum date",
9603     /**
9604      * @cfg {String} maxText
9605      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9606      */
9607     maxText : "This date is after the maximum date",
9608     /**
9609      * @cfg {String} format
9610      * The default date format string which can be overriden for localization support.  The format must be
9611      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9612      */
9613     format : "m/d/y",
9614     /**
9615      * @cfg {Array} disabledDays
9616      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9617      */
9618     disabledDays : null,
9619     /**
9620      * @cfg {String} disabledDaysText
9621      * The tooltip to display when the date falls on a disabled day (defaults to "")
9622      */
9623     disabledDaysText : "",
9624     /**
9625      * @cfg {RegExp} disabledDatesRE
9626      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9627      */
9628     disabledDatesRE : null,
9629     /**
9630      * @cfg {String} disabledDatesText
9631      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9632      */
9633     disabledDatesText : "",
9634     /**
9635      * @cfg {Boolean} constrainToViewport
9636      * True to constrain the date picker to the viewport (defaults to true)
9637      */
9638     constrainToViewport : true,
9639     /**
9640      * @cfg {Array} monthNames
9641      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9642      */
9643     monthNames : Date.monthNames,
9644     /**
9645      * @cfg {Array} dayNames
9646      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9647      */
9648     dayNames : Date.dayNames,
9649     /**
9650      * @cfg {String} nextText
9651      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9652      */
9653     nextText: 'Next Month (Control+Right)',
9654     /**
9655      * @cfg {String} prevText
9656      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9657      */
9658     prevText: 'Previous Month (Control+Left)',
9659     /**
9660      * @cfg {String} monthYearText
9661      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9662      */
9663     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9664     /**
9665      * @cfg {Number} startDay
9666      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9667      */
9668     startDay : 0,
9669     /**
9670      * @cfg {Bool} showClear
9671      * Show a clear button (usefull for date form elements that can be blank.)
9672      */
9673     
9674     showClear: false,
9675     
9676     /**
9677      * Sets the value of the date field
9678      * @param {Date} value The date to set
9679      */
9680     setValue : function(value){
9681         var old = this.value;
9682         
9683         if (typeof(value) == 'string') {
9684          
9685             value = Date.parseDate(value, this.format);
9686         }
9687         if (!value) {
9688             value = new Date();
9689         }
9690         
9691         this.value = value.clearTime(true);
9692         if(this.el){
9693             this.update(this.value);
9694         }
9695     },
9696
9697     /**
9698      * Gets the current selected value of the date field
9699      * @return {Date} The selected date
9700      */
9701     getValue : function(){
9702         return this.value;
9703     },
9704
9705     // private
9706     focus : function(){
9707         if(this.el){
9708             this.update(this.activeDate);
9709         }
9710     },
9711
9712     // privateval
9713     onRender : function(container, position){
9714         
9715         var m = [
9716              '<table cellspacing="0">',
9717                 '<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>',
9718                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9719         var dn = this.dayNames;
9720         for(var i = 0; i < 7; i++){
9721             var d = this.startDay+i;
9722             if(d > 6){
9723                 d = d-7;
9724             }
9725             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9726         }
9727         m[m.length] = "</tr></thead><tbody><tr>";
9728         for(var i = 0; i < 42; i++) {
9729             if(i % 7 == 0 && i != 0){
9730                 m[m.length] = "</tr><tr>";
9731             }
9732             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9733         }
9734         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9735             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9736
9737         var el = document.createElement("div");
9738         el.className = "x-date-picker";
9739         el.innerHTML = m.join("");
9740
9741         container.dom.insertBefore(el, position);
9742
9743         this.el = Roo.get(el);
9744         this.eventEl = Roo.get(el.firstChild);
9745
9746         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9747             handler: this.showPrevMonth,
9748             scope: this,
9749             preventDefault:true,
9750             stopDefault:true
9751         });
9752
9753         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9754             handler: this.showNextMonth,
9755             scope: this,
9756             preventDefault:true,
9757             stopDefault:true
9758         });
9759
9760         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9761
9762         this.monthPicker = this.el.down('div.x-date-mp');
9763         this.monthPicker.enableDisplayMode('block');
9764         
9765         var kn = new Roo.KeyNav(this.eventEl, {
9766             "left" : function(e){
9767                 e.ctrlKey ?
9768                     this.showPrevMonth() :
9769                     this.update(this.activeDate.add("d", -1));
9770             },
9771
9772             "right" : function(e){
9773                 e.ctrlKey ?
9774                     this.showNextMonth() :
9775                     this.update(this.activeDate.add("d", 1));
9776             },
9777
9778             "up" : function(e){
9779                 e.ctrlKey ?
9780                     this.showNextYear() :
9781                     this.update(this.activeDate.add("d", -7));
9782             },
9783
9784             "down" : function(e){
9785                 e.ctrlKey ?
9786                     this.showPrevYear() :
9787                     this.update(this.activeDate.add("d", 7));
9788             },
9789
9790             "pageUp" : function(e){
9791                 this.showNextMonth();
9792             },
9793
9794             "pageDown" : function(e){
9795                 this.showPrevMonth();
9796             },
9797
9798             "enter" : function(e){
9799                 e.stopPropagation();
9800                 return true;
9801             },
9802
9803             scope : this
9804         });
9805
9806         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9807
9808         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9809
9810         this.el.unselectable();
9811         
9812         this.cells = this.el.select("table.x-date-inner tbody td");
9813         this.textNodes = this.el.query("table.x-date-inner tbody span");
9814
9815         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9816             text: "&#160;",
9817             tooltip: this.monthYearText
9818         });
9819
9820         this.mbtn.on('click', this.showMonthPicker, this);
9821         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9822
9823
9824         var today = (new Date()).dateFormat(this.format);
9825         
9826         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9827         if (this.showClear) {
9828             baseTb.add( new Roo.Toolbar.Fill());
9829         }
9830         baseTb.add({
9831             text: String.format(this.todayText, today),
9832             tooltip: String.format(this.todayTip, today),
9833             handler: this.selectToday,
9834             scope: this
9835         });
9836         
9837         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9838             
9839         //});
9840         if (this.showClear) {
9841             
9842             baseTb.add( new Roo.Toolbar.Fill());
9843             baseTb.add({
9844                 text: '&#160;',
9845                 cls: 'x-btn-icon x-btn-clear',
9846                 handler: function() {
9847                     //this.value = '';
9848                     this.fireEvent("select", this, '');
9849                 },
9850                 scope: this
9851             });
9852         }
9853         
9854         
9855         if(Roo.isIE){
9856             this.el.repaint();
9857         }
9858         this.update(this.value);
9859     },
9860
9861     createMonthPicker : function(){
9862         if(!this.monthPicker.dom.firstChild){
9863             var buf = ['<table border="0" cellspacing="0">'];
9864             for(var i = 0; i < 6; i++){
9865                 buf.push(
9866                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9867                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9868                     i == 0 ?
9869                     '<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>' :
9870                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9871                 );
9872             }
9873             buf.push(
9874                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9875                     this.okText,
9876                     '</button><button type="button" class="x-date-mp-cancel">',
9877                     this.cancelText,
9878                     '</button></td></tr>',
9879                 '</table>'
9880             );
9881             this.monthPicker.update(buf.join(''));
9882             this.monthPicker.on('click', this.onMonthClick, this);
9883             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9884
9885             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9886             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9887
9888             this.mpMonths.each(function(m, a, i){
9889                 i += 1;
9890                 if((i%2) == 0){
9891                     m.dom.xmonth = 5 + Math.round(i * .5);
9892                 }else{
9893                     m.dom.xmonth = Math.round((i-1) * .5);
9894                 }
9895             });
9896         }
9897     },
9898
9899     showMonthPicker : function(){
9900         this.createMonthPicker();
9901         var size = this.el.getSize();
9902         this.monthPicker.setSize(size);
9903         this.monthPicker.child('table').setSize(size);
9904
9905         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9906         this.updateMPMonth(this.mpSelMonth);
9907         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9908         this.updateMPYear(this.mpSelYear);
9909
9910         this.monthPicker.slideIn('t', {duration:.2});
9911     },
9912
9913     updateMPYear : function(y){
9914         this.mpyear = y;
9915         var ys = this.mpYears.elements;
9916         for(var i = 1; i <= 10; i++){
9917             var td = ys[i-1], y2;
9918             if((i%2) == 0){
9919                 y2 = y + Math.round(i * .5);
9920                 td.firstChild.innerHTML = y2;
9921                 td.xyear = y2;
9922             }else{
9923                 y2 = y - (5-Math.round(i * .5));
9924                 td.firstChild.innerHTML = y2;
9925                 td.xyear = y2;
9926             }
9927             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9928         }
9929     },
9930
9931     updateMPMonth : function(sm){
9932         this.mpMonths.each(function(m, a, i){
9933             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9934         });
9935     },
9936
9937     selectMPMonth: function(m){
9938         
9939     },
9940
9941     onMonthClick : function(e, t){
9942         e.stopEvent();
9943         var el = new Roo.Element(t), pn;
9944         if(el.is('button.x-date-mp-cancel')){
9945             this.hideMonthPicker();
9946         }
9947         else if(el.is('button.x-date-mp-ok')){
9948             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9949             this.hideMonthPicker();
9950         }
9951         else if(pn = el.up('td.x-date-mp-month', 2)){
9952             this.mpMonths.removeClass('x-date-mp-sel');
9953             pn.addClass('x-date-mp-sel');
9954             this.mpSelMonth = pn.dom.xmonth;
9955         }
9956         else if(pn = el.up('td.x-date-mp-year', 2)){
9957             this.mpYears.removeClass('x-date-mp-sel');
9958             pn.addClass('x-date-mp-sel');
9959             this.mpSelYear = pn.dom.xyear;
9960         }
9961         else if(el.is('a.x-date-mp-prev')){
9962             this.updateMPYear(this.mpyear-10);
9963         }
9964         else if(el.is('a.x-date-mp-next')){
9965             this.updateMPYear(this.mpyear+10);
9966         }
9967     },
9968
9969     onMonthDblClick : function(e, t){
9970         e.stopEvent();
9971         var el = new Roo.Element(t), pn;
9972         if(pn = el.up('td.x-date-mp-month', 2)){
9973             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
9974             this.hideMonthPicker();
9975         }
9976         else if(pn = el.up('td.x-date-mp-year', 2)){
9977             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9978             this.hideMonthPicker();
9979         }
9980     },
9981
9982     hideMonthPicker : function(disableAnim){
9983         if(this.monthPicker){
9984             if(disableAnim === true){
9985                 this.monthPicker.hide();
9986             }else{
9987                 this.monthPicker.slideOut('t', {duration:.2});
9988             }
9989         }
9990     },
9991
9992     // private
9993     showPrevMonth : function(e){
9994         this.update(this.activeDate.add("mo", -1));
9995     },
9996
9997     // private
9998     showNextMonth : function(e){
9999         this.update(this.activeDate.add("mo", 1));
10000     },
10001
10002     // private
10003     showPrevYear : function(){
10004         this.update(this.activeDate.add("y", -1));
10005     },
10006
10007     // private
10008     showNextYear : function(){
10009         this.update(this.activeDate.add("y", 1));
10010     },
10011
10012     // private
10013     handleMouseWheel : function(e){
10014         var delta = e.getWheelDelta();
10015         if(delta > 0){
10016             this.showPrevMonth();
10017             e.stopEvent();
10018         } else if(delta < 0){
10019             this.showNextMonth();
10020             e.stopEvent();
10021         }
10022     },
10023
10024     // private
10025     handleDateClick : function(e, t){
10026         e.stopEvent();
10027         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10028             this.setValue(new Date(t.dateValue));
10029             this.fireEvent("select", this, this.value);
10030         }
10031     },
10032
10033     // private
10034     selectToday : function(){
10035         this.setValue(new Date().clearTime());
10036         this.fireEvent("select", this, this.value);
10037     },
10038
10039     // private
10040     update : function(date)
10041     {
10042         var vd = this.activeDate;
10043         this.activeDate = date;
10044         if(vd && this.el){
10045             var t = date.getTime();
10046             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10047                 this.cells.removeClass("x-date-selected");
10048                 this.cells.each(function(c){
10049                    if(c.dom.firstChild.dateValue == t){
10050                        c.addClass("x-date-selected");
10051                        setTimeout(function(){
10052                             try{c.dom.firstChild.focus();}catch(e){}
10053                        }, 50);
10054                        return false;
10055                    }
10056                 });
10057                 return;
10058             }
10059         }
10060         
10061         var days = date.getDaysInMonth();
10062         var firstOfMonth = date.getFirstDateOfMonth();
10063         var startingPos = firstOfMonth.getDay()-this.startDay;
10064
10065         if(startingPos <= this.startDay){
10066             startingPos += 7;
10067         }
10068
10069         var pm = date.add("mo", -1);
10070         var prevStart = pm.getDaysInMonth()-startingPos;
10071
10072         var cells = this.cells.elements;
10073         var textEls = this.textNodes;
10074         days += startingPos;
10075
10076         // convert everything to numbers so it's fast
10077         var day = 86400000;
10078         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10079         var today = new Date().clearTime().getTime();
10080         var sel = date.clearTime().getTime();
10081         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10082         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10083         var ddMatch = this.disabledDatesRE;
10084         var ddText = this.disabledDatesText;
10085         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10086         var ddaysText = this.disabledDaysText;
10087         var format = this.format;
10088
10089         var setCellClass = function(cal, cell){
10090             cell.title = "";
10091             var t = d.getTime();
10092             cell.firstChild.dateValue = t;
10093             if(t == today){
10094                 cell.className += " x-date-today";
10095                 cell.title = cal.todayText;
10096             }
10097             if(t == sel){
10098                 cell.className += " x-date-selected";
10099                 setTimeout(function(){
10100                     try{cell.firstChild.focus();}catch(e){}
10101                 }, 50);
10102             }
10103             // disabling
10104             if(t < min) {
10105                 cell.className = " x-date-disabled";
10106                 cell.title = cal.minText;
10107                 return;
10108             }
10109             if(t > max) {
10110                 cell.className = " x-date-disabled";
10111                 cell.title = cal.maxText;
10112                 return;
10113             }
10114             if(ddays){
10115                 if(ddays.indexOf(d.getDay()) != -1){
10116                     cell.title = ddaysText;
10117                     cell.className = " x-date-disabled";
10118                 }
10119             }
10120             if(ddMatch && format){
10121                 var fvalue = d.dateFormat(format);
10122                 if(ddMatch.test(fvalue)){
10123                     cell.title = ddText.replace("%0", fvalue);
10124                     cell.className = " x-date-disabled";
10125                 }
10126             }
10127         };
10128
10129         var i = 0;
10130         for(; i < startingPos; i++) {
10131             textEls[i].innerHTML = (++prevStart);
10132             d.setDate(d.getDate()+1);
10133             cells[i].className = "x-date-prevday";
10134             setCellClass(this, cells[i]);
10135         }
10136         for(; i < days; i++){
10137             intDay = i - startingPos + 1;
10138             textEls[i].innerHTML = (intDay);
10139             d.setDate(d.getDate()+1);
10140             cells[i].className = "x-date-active";
10141             setCellClass(this, cells[i]);
10142         }
10143         var extraDays = 0;
10144         for(; i < 42; i++) {
10145              textEls[i].innerHTML = (++extraDays);
10146              d.setDate(d.getDate()+1);
10147              cells[i].className = "x-date-nextday";
10148              setCellClass(this, cells[i]);
10149         }
10150
10151         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10152         this.fireEvent('monthchange', this, date);
10153         
10154         if(!this.internalRender){
10155             var main = this.el.dom.firstChild;
10156             var w = main.offsetWidth;
10157             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10158             Roo.fly(main).setWidth(w);
10159             this.internalRender = true;
10160             // opera does not respect the auto grow header center column
10161             // then, after it gets a width opera refuses to recalculate
10162             // without a second pass
10163             if(Roo.isOpera && !this.secondPass){
10164                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10165                 this.secondPass = true;
10166                 this.update.defer(10, this, [date]);
10167             }
10168         }
10169         
10170         
10171     }
10172 });        /*
10173  * Based on:
10174  * Ext JS Library 1.1.1
10175  * Copyright(c) 2006-2007, Ext JS, LLC.
10176  *
10177  * Originally Released Under LGPL - original licence link has changed is not relivant.
10178  *
10179  * Fork - LGPL
10180  * <script type="text/javascript">
10181  */
10182 /**
10183  * @class Roo.TabPanel
10184  * @extends Roo.util.Observable
10185  * A lightweight tab container.
10186  * <br><br>
10187  * Usage:
10188  * <pre><code>
10189 // basic tabs 1, built from existing content
10190 var tabs = new Roo.TabPanel("tabs1");
10191 tabs.addTab("script", "View Script");
10192 tabs.addTab("markup", "View Markup");
10193 tabs.activate("script");
10194
10195 // more advanced tabs, built from javascript
10196 var jtabs = new Roo.TabPanel("jtabs");
10197 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10198
10199 // set up the UpdateManager
10200 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10201 var updater = tab2.getUpdateManager();
10202 updater.setDefaultUrl("ajax1.htm");
10203 tab2.on('activate', updater.refresh, updater, true);
10204
10205 // Use setUrl for Ajax loading
10206 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10207 tab3.setUrl("ajax2.htm", null, true);
10208
10209 // Disabled tab
10210 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10211 tab4.disable();
10212
10213 jtabs.activate("jtabs-1");
10214  * </code></pre>
10215  * @constructor
10216  * Create a new TabPanel.
10217  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10218  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10219  */
10220 Roo.TabPanel = function(container, config){
10221     /**
10222     * The container element for this TabPanel.
10223     * @type Roo.Element
10224     */
10225     this.el = Roo.get(container, true);
10226     if(config){
10227         if(typeof config == "boolean"){
10228             this.tabPosition = config ? "bottom" : "top";
10229         }else{
10230             Roo.apply(this, config);
10231         }
10232     }
10233     if(this.tabPosition == "bottom"){
10234         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10235         this.el.addClass("x-tabs-bottom");
10236     }
10237     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10238     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10239     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10240     if(Roo.isIE){
10241         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10242     }
10243     if(this.tabPosition != "bottom"){
10244         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10245          * @type Roo.Element
10246          */
10247         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10248         this.el.addClass("x-tabs-top");
10249     }
10250     this.items = [];
10251
10252     this.bodyEl.setStyle("position", "relative");
10253
10254     this.active = null;
10255     this.activateDelegate = this.activate.createDelegate(this);
10256
10257     this.addEvents({
10258         /**
10259          * @event tabchange
10260          * Fires when the active tab changes
10261          * @param {Roo.TabPanel} this
10262          * @param {Roo.TabPanelItem} activePanel The new active tab
10263          */
10264         "tabchange": true,
10265         /**
10266          * @event beforetabchange
10267          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10268          * @param {Roo.TabPanel} this
10269          * @param {Object} e Set cancel to true on this object to cancel the tab change
10270          * @param {Roo.TabPanelItem} tab The tab being changed to
10271          */
10272         "beforetabchange" : true
10273     });
10274
10275     Roo.EventManager.onWindowResize(this.onResize, this);
10276     this.cpad = this.el.getPadding("lr");
10277     this.hiddenCount = 0;
10278
10279
10280     // toolbar on the tabbar support...
10281     if (this.toolbar) {
10282         var tcfg = this.toolbar;
10283         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10284         this.toolbar = new Roo.Toolbar(tcfg);
10285         if (Roo.isSafari) {
10286             var tbl = tcfg.container.child('table', true);
10287             tbl.setAttribute('width', '100%');
10288         }
10289         
10290     }
10291    
10292
10293
10294     Roo.TabPanel.superclass.constructor.call(this);
10295 };
10296
10297 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10298     /*
10299      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10300      */
10301     tabPosition : "top",
10302     /*
10303      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10304      */
10305     currentTabWidth : 0,
10306     /*
10307      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10308      */
10309     minTabWidth : 40,
10310     /*
10311      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10312      */
10313     maxTabWidth : 250,
10314     /*
10315      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10316      */
10317     preferredTabWidth : 175,
10318     /*
10319      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10320      */
10321     resizeTabs : false,
10322     /*
10323      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10324      */
10325     monitorResize : true,
10326     /*
10327      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10328      */
10329     toolbar : false,
10330
10331     /**
10332      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10333      * @param {String} id The id of the div to use <b>or create</b>
10334      * @param {String} text The text for the tab
10335      * @param {String} content (optional) Content to put in the TabPanelItem body
10336      * @param {Boolean} closable (optional) True to create a close icon on the tab
10337      * @return {Roo.TabPanelItem} The created TabPanelItem
10338      */
10339     addTab : function(id, text, content, closable){
10340         var item = new Roo.TabPanelItem(this, id, text, closable);
10341         this.addTabItem(item);
10342         if(content){
10343             item.setContent(content);
10344         }
10345         return item;
10346     },
10347
10348     /**
10349      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10350      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10351      * @return {Roo.TabPanelItem}
10352      */
10353     getTab : function(id){
10354         return this.items[id];
10355     },
10356
10357     /**
10358      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10359      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10360      */
10361     hideTab : function(id){
10362         var t = this.items[id];
10363         if(!t.isHidden()){
10364            t.setHidden(true);
10365            this.hiddenCount++;
10366            this.autoSizeTabs();
10367         }
10368     },
10369
10370     /**
10371      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10372      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10373      */
10374     unhideTab : function(id){
10375         var t = this.items[id];
10376         if(t.isHidden()){
10377            t.setHidden(false);
10378            this.hiddenCount--;
10379            this.autoSizeTabs();
10380         }
10381     },
10382
10383     /**
10384      * Adds an existing {@link Roo.TabPanelItem}.
10385      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10386      */
10387     addTabItem : function(item){
10388         this.items[item.id] = item;
10389         this.items.push(item);
10390         if(this.resizeTabs){
10391            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10392            this.autoSizeTabs();
10393         }else{
10394             item.autoSize();
10395         }
10396     },
10397
10398     /**
10399      * Removes a {@link Roo.TabPanelItem}.
10400      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10401      */
10402     removeTab : function(id){
10403         var items = this.items;
10404         var tab = items[id];
10405         if(!tab) { return; }
10406         var index = items.indexOf(tab);
10407         if(this.active == tab && items.length > 1){
10408             var newTab = this.getNextAvailable(index);
10409             if(newTab) {
10410                 newTab.activate();
10411             }
10412         }
10413         this.stripEl.dom.removeChild(tab.pnode.dom);
10414         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10415             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10416         }
10417         items.splice(index, 1);
10418         delete this.items[tab.id];
10419         tab.fireEvent("close", tab);
10420         tab.purgeListeners();
10421         this.autoSizeTabs();
10422     },
10423
10424     getNextAvailable : function(start){
10425         var items = this.items;
10426         var index = start;
10427         // look for a next tab that will slide over to
10428         // replace the one being removed
10429         while(index < items.length){
10430             var item = items[++index];
10431             if(item && !item.isHidden()){
10432                 return item;
10433             }
10434         }
10435         // if one isn't found select the previous tab (on the left)
10436         index = start;
10437         while(index >= 0){
10438             var item = items[--index];
10439             if(item && !item.isHidden()){
10440                 return item;
10441             }
10442         }
10443         return null;
10444     },
10445
10446     /**
10447      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10448      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10449      */
10450     disableTab : function(id){
10451         var tab = this.items[id];
10452         if(tab && this.active != tab){
10453             tab.disable();
10454         }
10455     },
10456
10457     /**
10458      * Enables a {@link Roo.TabPanelItem} that is disabled.
10459      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10460      */
10461     enableTab : function(id){
10462         var tab = this.items[id];
10463         tab.enable();
10464     },
10465
10466     /**
10467      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10468      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10469      * @return {Roo.TabPanelItem} The TabPanelItem.
10470      */
10471     activate : function(id){
10472         var tab = this.items[id];
10473         if(!tab){
10474             return null;
10475         }
10476         if(tab == this.active || tab.disabled){
10477             return tab;
10478         }
10479         var e = {};
10480         this.fireEvent("beforetabchange", this, e, tab);
10481         if(e.cancel !== true && !tab.disabled){
10482             if(this.active){
10483                 this.active.hide();
10484             }
10485             this.active = this.items[id];
10486             this.active.show();
10487             this.fireEvent("tabchange", this, this.active);
10488         }
10489         return tab;
10490     },
10491
10492     /**
10493      * Gets the active {@link Roo.TabPanelItem}.
10494      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10495      */
10496     getActiveTab : function(){
10497         return this.active;
10498     },
10499
10500     /**
10501      * Updates the tab body element to fit the height of the container element
10502      * for overflow scrolling
10503      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10504      */
10505     syncHeight : function(targetHeight){
10506         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10507         var bm = this.bodyEl.getMargins();
10508         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10509         this.bodyEl.setHeight(newHeight);
10510         return newHeight;
10511     },
10512
10513     onResize : function(){
10514         if(this.monitorResize){
10515             this.autoSizeTabs();
10516         }
10517     },
10518
10519     /**
10520      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10521      */
10522     beginUpdate : function(){
10523         this.updating = true;
10524     },
10525
10526     /**
10527      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10528      */
10529     endUpdate : function(){
10530         this.updating = false;
10531         this.autoSizeTabs();
10532     },
10533
10534     /**
10535      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10536      */
10537     autoSizeTabs : function(){
10538         var count = this.items.length;
10539         var vcount = count - this.hiddenCount;
10540         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10541         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10542         var availWidth = Math.floor(w / vcount);
10543         var b = this.stripBody;
10544         if(b.getWidth() > w){
10545             var tabs = this.items;
10546             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10547             if(availWidth < this.minTabWidth){
10548                 /*if(!this.sleft){    // incomplete scrolling code
10549                     this.createScrollButtons();
10550                 }
10551                 this.showScroll();
10552                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10553             }
10554         }else{
10555             if(this.currentTabWidth < this.preferredTabWidth){
10556                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10557             }
10558         }
10559     },
10560
10561     /**
10562      * Returns the number of tabs in this TabPanel.
10563      * @return {Number}
10564      */
10565      getCount : function(){
10566          return this.items.length;
10567      },
10568
10569     /**
10570      * Resizes all the tabs to the passed width
10571      * @param {Number} The new width
10572      */
10573     setTabWidth : function(width){
10574         this.currentTabWidth = width;
10575         for(var i = 0, len = this.items.length; i < len; i++) {
10576                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10577         }
10578     },
10579
10580     /**
10581      * Destroys this TabPanel
10582      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10583      */
10584     destroy : function(removeEl){
10585         Roo.EventManager.removeResizeListener(this.onResize, this);
10586         for(var i = 0, len = this.items.length; i < len; i++){
10587             this.items[i].purgeListeners();
10588         }
10589         if(removeEl === true){
10590             this.el.update("");
10591             this.el.remove();
10592         }
10593     }
10594 });
10595
10596 /**
10597  * @class Roo.TabPanelItem
10598  * @extends Roo.util.Observable
10599  * Represents an individual item (tab plus body) in a TabPanel.
10600  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10601  * @param {String} id The id of this TabPanelItem
10602  * @param {String} text The text for the tab of this TabPanelItem
10603  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10604  */
10605 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10606     /**
10607      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10608      * @type Roo.TabPanel
10609      */
10610     this.tabPanel = tabPanel;
10611     /**
10612      * The id for this TabPanelItem
10613      * @type String
10614      */
10615     this.id = id;
10616     /** @private */
10617     this.disabled = false;
10618     /** @private */
10619     this.text = text;
10620     /** @private */
10621     this.loaded = false;
10622     this.closable = closable;
10623
10624     /**
10625      * The body element for this TabPanelItem.
10626      * @type Roo.Element
10627      */
10628     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10629     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10630     this.bodyEl.setStyle("display", "block");
10631     this.bodyEl.setStyle("zoom", "1");
10632     this.hideAction();
10633
10634     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10635     /** @private */
10636     this.el = Roo.get(els.el, true);
10637     this.inner = Roo.get(els.inner, true);
10638     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10639     this.pnode = Roo.get(els.el.parentNode, true);
10640     this.el.on("mousedown", this.onTabMouseDown, this);
10641     this.el.on("click", this.onTabClick, this);
10642     /** @private */
10643     if(closable){
10644         var c = Roo.get(els.close, true);
10645         c.dom.title = this.closeText;
10646         c.addClassOnOver("close-over");
10647         c.on("click", this.closeClick, this);
10648      }
10649
10650     this.addEvents({
10651          /**
10652          * @event activate
10653          * Fires when this tab becomes the active tab.
10654          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10655          * @param {Roo.TabPanelItem} this
10656          */
10657         "activate": true,
10658         /**
10659          * @event beforeclose
10660          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10661          * @param {Roo.TabPanelItem} this
10662          * @param {Object} e Set cancel to true on this object to cancel the close.
10663          */
10664         "beforeclose": true,
10665         /**
10666          * @event close
10667          * Fires when this tab is closed.
10668          * @param {Roo.TabPanelItem} this
10669          */
10670          "close": true,
10671         /**
10672          * @event deactivate
10673          * Fires when this tab is no longer the active tab.
10674          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10675          * @param {Roo.TabPanelItem} this
10676          */
10677          "deactivate" : true
10678     });
10679     this.hidden = false;
10680
10681     Roo.TabPanelItem.superclass.constructor.call(this);
10682 };
10683
10684 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10685     purgeListeners : function(){
10686        Roo.util.Observable.prototype.purgeListeners.call(this);
10687        this.el.removeAllListeners();
10688     },
10689     /**
10690      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10691      */
10692     show : function(){
10693         this.pnode.addClass("on");
10694         this.showAction();
10695         if(Roo.isOpera){
10696             this.tabPanel.stripWrap.repaint();
10697         }
10698         this.fireEvent("activate", this.tabPanel, this);
10699     },
10700
10701     /**
10702      * Returns true if this tab is the active tab.
10703      * @return {Boolean}
10704      */
10705     isActive : function(){
10706         return this.tabPanel.getActiveTab() == this;
10707     },
10708
10709     /**
10710      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10711      */
10712     hide : function(){
10713         this.pnode.removeClass("on");
10714         this.hideAction();
10715         this.fireEvent("deactivate", this.tabPanel, this);
10716     },
10717
10718     hideAction : function(){
10719         this.bodyEl.hide();
10720         this.bodyEl.setStyle("position", "absolute");
10721         this.bodyEl.setLeft("-20000px");
10722         this.bodyEl.setTop("-20000px");
10723     },
10724
10725     showAction : function(){
10726         this.bodyEl.setStyle("position", "relative");
10727         this.bodyEl.setTop("");
10728         this.bodyEl.setLeft("");
10729         this.bodyEl.show();
10730     },
10731
10732     /**
10733      * Set the tooltip for the tab.
10734      * @param {String} tooltip The tab's tooltip
10735      */
10736     setTooltip : function(text){
10737         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10738             this.textEl.dom.qtip = text;
10739             this.textEl.dom.removeAttribute('title');
10740         }else{
10741             this.textEl.dom.title = text;
10742         }
10743     },
10744
10745     onTabClick : function(e){
10746         e.preventDefault();
10747         this.tabPanel.activate(this.id);
10748     },
10749
10750     onTabMouseDown : function(e){
10751         e.preventDefault();
10752         this.tabPanel.activate(this.id);
10753     },
10754
10755     getWidth : function(){
10756         return this.inner.getWidth();
10757     },
10758
10759     setWidth : function(width){
10760         var iwidth = width - this.pnode.getPadding("lr");
10761         this.inner.setWidth(iwidth);
10762         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10763         this.pnode.setWidth(width);
10764     },
10765
10766     /**
10767      * Show or hide the tab
10768      * @param {Boolean} hidden True to hide or false to show.
10769      */
10770     setHidden : function(hidden){
10771         this.hidden = hidden;
10772         this.pnode.setStyle("display", hidden ? "none" : "");
10773     },
10774
10775     /**
10776      * Returns true if this tab is "hidden"
10777      * @return {Boolean}
10778      */
10779     isHidden : function(){
10780         return this.hidden;
10781     },
10782
10783     /**
10784      * Returns the text for this tab
10785      * @return {String}
10786      */
10787     getText : function(){
10788         return this.text;
10789     },
10790
10791     autoSize : function(){
10792         //this.el.beginMeasure();
10793         this.textEl.setWidth(1);
10794         /*
10795          *  #2804 [new] Tabs in Roojs
10796          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10797          */
10798         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10799         //this.el.endMeasure();
10800     },
10801
10802     /**
10803      * Sets the text for the tab (Note: this also sets the tooltip text)
10804      * @param {String} text The tab's text and tooltip
10805      */
10806     setText : function(text){
10807         this.text = text;
10808         this.textEl.update(text);
10809         this.setTooltip(text);
10810         if(!this.tabPanel.resizeTabs){
10811             this.autoSize();
10812         }
10813     },
10814     /**
10815      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10816      */
10817     activate : function(){
10818         this.tabPanel.activate(this.id);
10819     },
10820
10821     /**
10822      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10823      */
10824     disable : function(){
10825         if(this.tabPanel.active != this){
10826             this.disabled = true;
10827             this.pnode.addClass("disabled");
10828         }
10829     },
10830
10831     /**
10832      * Enables this TabPanelItem if it was previously disabled.
10833      */
10834     enable : function(){
10835         this.disabled = false;
10836         this.pnode.removeClass("disabled");
10837     },
10838
10839     /**
10840      * Sets the content for this TabPanelItem.
10841      * @param {String} content The content
10842      * @param {Boolean} loadScripts true to look for and load scripts
10843      */
10844     setContent : function(content, loadScripts){
10845         this.bodyEl.update(content, loadScripts);
10846     },
10847
10848     /**
10849      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10850      * @return {Roo.UpdateManager} The UpdateManager
10851      */
10852     getUpdateManager : function(){
10853         return this.bodyEl.getUpdateManager();
10854     },
10855
10856     /**
10857      * Set a URL to be used to load the content for this TabPanelItem.
10858      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10859      * @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)
10860      * @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)
10861      * @return {Roo.UpdateManager} The UpdateManager
10862      */
10863     setUrl : function(url, params, loadOnce){
10864         if(this.refreshDelegate){
10865             this.un('activate', this.refreshDelegate);
10866         }
10867         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10868         this.on("activate", this.refreshDelegate);
10869         return this.bodyEl.getUpdateManager();
10870     },
10871
10872     /** @private */
10873     _handleRefresh : function(url, params, loadOnce){
10874         if(!loadOnce || !this.loaded){
10875             var updater = this.bodyEl.getUpdateManager();
10876             updater.update(url, params, this._setLoaded.createDelegate(this));
10877         }
10878     },
10879
10880     /**
10881      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10882      *   Will fail silently if the setUrl method has not been called.
10883      *   This does not activate the panel, just updates its content.
10884      */
10885     refresh : function(){
10886         if(this.refreshDelegate){
10887            this.loaded = false;
10888            this.refreshDelegate();
10889         }
10890     },
10891
10892     /** @private */
10893     _setLoaded : function(){
10894         this.loaded = true;
10895     },
10896
10897     /** @private */
10898     closeClick : function(e){
10899         var o = {};
10900         e.stopEvent();
10901         this.fireEvent("beforeclose", this, o);
10902         if(o.cancel !== true){
10903             this.tabPanel.removeTab(this.id);
10904         }
10905     },
10906     /**
10907      * The text displayed in the tooltip for the close icon.
10908      * @type String
10909      */
10910     closeText : "Close this tab"
10911 });
10912
10913 /** @private */
10914 Roo.TabPanel.prototype.createStrip = function(container){
10915     var strip = document.createElement("div");
10916     strip.className = "x-tabs-wrap";
10917     container.appendChild(strip);
10918     return strip;
10919 };
10920 /** @private */
10921 Roo.TabPanel.prototype.createStripList = function(strip){
10922     // div wrapper for retard IE
10923     // returns the "tr" element.
10924     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10925         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10926         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10927     return strip.firstChild.firstChild.firstChild.firstChild;
10928 };
10929 /** @private */
10930 Roo.TabPanel.prototype.createBody = function(container){
10931     var body = document.createElement("div");
10932     Roo.id(body, "tab-body");
10933     Roo.fly(body).addClass("x-tabs-body");
10934     container.appendChild(body);
10935     return body;
10936 };
10937 /** @private */
10938 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10939     var body = Roo.getDom(id);
10940     if(!body){
10941         body = document.createElement("div");
10942         body.id = id;
10943     }
10944     Roo.fly(body).addClass("x-tabs-item-body");
10945     bodyEl.insertBefore(body, bodyEl.firstChild);
10946     return body;
10947 };
10948 /** @private */
10949 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10950     var td = document.createElement("td");
10951     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10952     //stripEl.appendChild(td);
10953     if(closable){
10954         td.className = "x-tabs-closable";
10955         if(!this.closeTpl){
10956             this.closeTpl = new Roo.Template(
10957                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10958                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10959                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10960             );
10961         }
10962         var el = this.closeTpl.overwrite(td, {"text": text});
10963         var close = el.getElementsByTagName("div")[0];
10964         var inner = el.getElementsByTagName("em")[0];
10965         return {"el": el, "close": close, "inner": inner};
10966     } else {
10967         if(!this.tabTpl){
10968             this.tabTpl = new Roo.Template(
10969                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10970                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
10971             );
10972         }
10973         var el = this.tabTpl.overwrite(td, {"text": text});
10974         var inner = el.getElementsByTagName("em")[0];
10975         return {"el": el, "inner": inner};
10976     }
10977 };/*
10978  * Based on:
10979  * Ext JS Library 1.1.1
10980  * Copyright(c) 2006-2007, Ext JS, LLC.
10981  *
10982  * Originally Released Under LGPL - original licence link has changed is not relivant.
10983  *
10984  * Fork - LGPL
10985  * <script type="text/javascript">
10986  */
10987
10988 /**
10989  * @class Roo.Button
10990  * @extends Roo.util.Observable
10991  * Simple Button class
10992  * @cfg {String} text The button text
10993  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
10994  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
10995  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
10996  * @cfg {Object} scope The scope of the handler
10997  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
10998  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
10999  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11000  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11001  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11002  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11003    applies if enableToggle = true)
11004  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11005  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11006   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11007  * @constructor
11008  * Create a new button
11009  * @param {Object} config The config object
11010  */
11011 Roo.Button = function(renderTo, config)
11012 {
11013     if (!config) {
11014         config = renderTo;
11015         renderTo = config.renderTo || false;
11016     }
11017     
11018     Roo.apply(this, config);
11019     this.addEvents({
11020         /**
11021              * @event click
11022              * Fires when this button is clicked
11023              * @param {Button} this
11024              * @param {EventObject} e The click event
11025              */
11026             "click" : true,
11027         /**
11028              * @event toggle
11029              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11030              * @param {Button} this
11031              * @param {Boolean} pressed
11032              */
11033             "toggle" : true,
11034         /**
11035              * @event mouseover
11036              * Fires when the mouse hovers over the button
11037              * @param {Button} this
11038              * @param {Event} e The event object
11039              */
11040         'mouseover' : true,
11041         /**
11042              * @event mouseout
11043              * Fires when the mouse exits the button
11044              * @param {Button} this
11045              * @param {Event} e The event object
11046              */
11047         'mouseout': true,
11048          /**
11049              * @event render
11050              * Fires when the button is rendered
11051              * @param {Button} this
11052              */
11053         'render': true
11054     });
11055     if(this.menu){
11056         this.menu = Roo.menu.MenuMgr.get(this.menu);
11057     }
11058     // register listeners first!!  - so render can be captured..
11059     Roo.util.Observable.call(this);
11060     if(renderTo){
11061         this.render(renderTo);
11062     }
11063     
11064   
11065 };
11066
11067 Roo.extend(Roo.Button, Roo.util.Observable, {
11068     /**
11069      * 
11070      */
11071     
11072     /**
11073      * Read-only. True if this button is hidden
11074      * @type Boolean
11075      */
11076     hidden : false,
11077     /**
11078      * Read-only. True if this button is disabled
11079      * @type Boolean
11080      */
11081     disabled : false,
11082     /**
11083      * Read-only. True if this button is pressed (only if enableToggle = true)
11084      * @type Boolean
11085      */
11086     pressed : false,
11087
11088     /**
11089      * @cfg {Number} tabIndex 
11090      * The DOM tabIndex for this button (defaults to undefined)
11091      */
11092     tabIndex : undefined,
11093
11094     /**
11095      * @cfg {Boolean} enableToggle
11096      * True to enable pressed/not pressed toggling (defaults to false)
11097      */
11098     enableToggle: false,
11099     /**
11100      * @cfg {Mixed} menu
11101      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11102      */
11103     menu : undefined,
11104     /**
11105      * @cfg {String} menuAlign
11106      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11107      */
11108     menuAlign : "tl-bl?",
11109
11110     /**
11111      * @cfg {String} iconCls
11112      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11113      */
11114     iconCls : undefined,
11115     /**
11116      * @cfg {String} type
11117      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11118      */
11119     type : 'button',
11120
11121     // private
11122     menuClassTarget: 'tr',
11123
11124     /**
11125      * @cfg {String} clickEvent
11126      * The type of event to map to the button's event handler (defaults to 'click')
11127      */
11128     clickEvent : 'click',
11129
11130     /**
11131      * @cfg {Boolean} handleMouseEvents
11132      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11133      */
11134     handleMouseEvents : true,
11135
11136     /**
11137      * @cfg {String} tooltipType
11138      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11139      */
11140     tooltipType : 'qtip',
11141
11142     /**
11143      * @cfg {String} cls
11144      * A CSS class to apply to the button's main element.
11145      */
11146     
11147     /**
11148      * @cfg {Roo.Template} template (Optional)
11149      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11150      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11151      * require code modifications if required elements (e.g. a button) aren't present.
11152      */
11153
11154     // private
11155     render : function(renderTo){
11156         var btn;
11157         if(this.hideParent){
11158             this.parentEl = Roo.get(renderTo);
11159         }
11160         if(!this.dhconfig){
11161             if(!this.template){
11162                 if(!Roo.Button.buttonTemplate){
11163                     // hideous table template
11164                     Roo.Button.buttonTemplate = new Roo.Template(
11165                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11166                         '<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>',
11167                         "</tr></tbody></table>");
11168                 }
11169                 this.template = Roo.Button.buttonTemplate;
11170             }
11171             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11172             var btnEl = btn.child("button:first");
11173             btnEl.on('focus', this.onFocus, this);
11174             btnEl.on('blur', this.onBlur, this);
11175             if(this.cls){
11176                 btn.addClass(this.cls);
11177             }
11178             if(this.icon){
11179                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11180             }
11181             if(this.iconCls){
11182                 btnEl.addClass(this.iconCls);
11183                 if(!this.cls){
11184                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11185                 }
11186             }
11187             if(this.tabIndex !== undefined){
11188                 btnEl.dom.tabIndex = this.tabIndex;
11189             }
11190             if(this.tooltip){
11191                 if(typeof this.tooltip == 'object'){
11192                     Roo.QuickTips.tips(Roo.apply({
11193                           target: btnEl.id
11194                     }, this.tooltip));
11195                 } else {
11196                     btnEl.dom[this.tooltipType] = this.tooltip;
11197                 }
11198             }
11199         }else{
11200             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11201         }
11202         this.el = btn;
11203         if(this.id){
11204             this.el.dom.id = this.el.id = this.id;
11205         }
11206         if(this.menu){
11207             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11208             this.menu.on("show", this.onMenuShow, this);
11209             this.menu.on("hide", this.onMenuHide, this);
11210         }
11211         btn.addClass("x-btn");
11212         if(Roo.isIE && !Roo.isIE7){
11213             this.autoWidth.defer(1, this);
11214         }else{
11215             this.autoWidth();
11216         }
11217         if(this.handleMouseEvents){
11218             btn.on("mouseover", this.onMouseOver, this);
11219             btn.on("mouseout", this.onMouseOut, this);
11220             btn.on("mousedown", this.onMouseDown, this);
11221         }
11222         btn.on(this.clickEvent, this.onClick, this);
11223         //btn.on("mouseup", this.onMouseUp, this);
11224         if(this.hidden){
11225             this.hide();
11226         }
11227         if(this.disabled){
11228             this.disable();
11229         }
11230         Roo.ButtonToggleMgr.register(this);
11231         if(this.pressed){
11232             this.el.addClass("x-btn-pressed");
11233         }
11234         if(this.repeat){
11235             var repeater = new Roo.util.ClickRepeater(btn,
11236                 typeof this.repeat == "object" ? this.repeat : {}
11237             );
11238             repeater.on("click", this.onClick,  this);
11239         }
11240         
11241         this.fireEvent('render', this);
11242         
11243     },
11244     /**
11245      * Returns the button's underlying element
11246      * @return {Roo.Element} The element
11247      */
11248     getEl : function(){
11249         return this.el;  
11250     },
11251     
11252     /**
11253      * Destroys this Button and removes any listeners.
11254      */
11255     destroy : function(){
11256         Roo.ButtonToggleMgr.unregister(this);
11257         this.el.removeAllListeners();
11258         this.purgeListeners();
11259         this.el.remove();
11260     },
11261
11262     // private
11263     autoWidth : function(){
11264         if(this.el){
11265             this.el.setWidth("auto");
11266             if(Roo.isIE7 && Roo.isStrict){
11267                 var ib = this.el.child('button');
11268                 if(ib && ib.getWidth() > 20){
11269                     ib.clip();
11270                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11271                 }
11272             }
11273             if(this.minWidth){
11274                 if(this.hidden){
11275                     this.el.beginMeasure();
11276                 }
11277                 if(this.el.getWidth() < this.minWidth){
11278                     this.el.setWidth(this.minWidth);
11279                 }
11280                 if(this.hidden){
11281                     this.el.endMeasure();
11282                 }
11283             }
11284         }
11285     },
11286
11287     /**
11288      * Assigns this button's click handler
11289      * @param {Function} handler The function to call when the button is clicked
11290      * @param {Object} scope (optional) Scope for the function passed in
11291      */
11292     setHandler : function(handler, scope){
11293         this.handler = handler;
11294         this.scope = scope;  
11295     },
11296     
11297     /**
11298      * Sets this button's text
11299      * @param {String} text The button text
11300      */
11301     setText : function(text){
11302         this.text = text;
11303         if(this.el){
11304             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11305         }
11306         this.autoWidth();
11307     },
11308     
11309     /**
11310      * Gets the text for this button
11311      * @return {String} The button text
11312      */
11313     getText : function(){
11314         return this.text;  
11315     },
11316     
11317     /**
11318      * Show this button
11319      */
11320     show: function(){
11321         this.hidden = false;
11322         if(this.el){
11323             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11324         }
11325     },
11326     
11327     /**
11328      * Hide this button
11329      */
11330     hide: function(){
11331         this.hidden = true;
11332         if(this.el){
11333             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11334         }
11335     },
11336     
11337     /**
11338      * Convenience function for boolean show/hide
11339      * @param {Boolean} visible True to show, false to hide
11340      */
11341     setVisible: function(visible){
11342         if(visible) {
11343             this.show();
11344         }else{
11345             this.hide();
11346         }
11347     },
11348     
11349     /**
11350      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11351      * @param {Boolean} state (optional) Force a particular state
11352      */
11353     toggle : function(state){
11354         state = state === undefined ? !this.pressed : state;
11355         if(state != this.pressed){
11356             if(state){
11357                 this.el.addClass("x-btn-pressed");
11358                 this.pressed = true;
11359                 this.fireEvent("toggle", this, true);
11360             }else{
11361                 this.el.removeClass("x-btn-pressed");
11362                 this.pressed = false;
11363                 this.fireEvent("toggle", this, false);
11364             }
11365             if(this.toggleHandler){
11366                 this.toggleHandler.call(this.scope || this, this, state);
11367             }
11368         }
11369     },
11370     
11371     /**
11372      * Focus the button
11373      */
11374     focus : function(){
11375         this.el.child('button:first').focus();
11376     },
11377     
11378     /**
11379      * Disable this button
11380      */
11381     disable : function(){
11382         if(this.el){
11383             this.el.addClass("x-btn-disabled");
11384         }
11385         this.disabled = true;
11386     },
11387     
11388     /**
11389      * Enable this button
11390      */
11391     enable : function(){
11392         if(this.el){
11393             this.el.removeClass("x-btn-disabled");
11394         }
11395         this.disabled = false;
11396     },
11397
11398     /**
11399      * Convenience function for boolean enable/disable
11400      * @param {Boolean} enabled True to enable, false to disable
11401      */
11402     setDisabled : function(v){
11403         this[v !== true ? "enable" : "disable"]();
11404     },
11405
11406     // private
11407     onClick : function(e){
11408         if(e){
11409             e.preventDefault();
11410         }
11411         if(e.button != 0){
11412             return;
11413         }
11414         if(!this.disabled){
11415             if(this.enableToggle){
11416                 this.toggle();
11417             }
11418             if(this.menu && !this.menu.isVisible()){
11419                 this.menu.show(this.el, this.menuAlign);
11420             }
11421             this.fireEvent("click", this, e);
11422             if(this.handler){
11423                 this.el.removeClass("x-btn-over");
11424                 this.handler.call(this.scope || this, this, e);
11425             }
11426         }
11427     },
11428     // private
11429     onMouseOver : function(e){
11430         if(!this.disabled){
11431             this.el.addClass("x-btn-over");
11432             this.fireEvent('mouseover', this, e);
11433         }
11434     },
11435     // private
11436     onMouseOut : function(e){
11437         if(!e.within(this.el,  true)){
11438             this.el.removeClass("x-btn-over");
11439             this.fireEvent('mouseout', this, e);
11440         }
11441     },
11442     // private
11443     onFocus : function(e){
11444         if(!this.disabled){
11445             this.el.addClass("x-btn-focus");
11446         }
11447     },
11448     // private
11449     onBlur : function(e){
11450         this.el.removeClass("x-btn-focus");
11451     },
11452     // private
11453     onMouseDown : function(e){
11454         if(!this.disabled && e.button == 0){
11455             this.el.addClass("x-btn-click");
11456             Roo.get(document).on('mouseup', this.onMouseUp, this);
11457         }
11458     },
11459     // private
11460     onMouseUp : function(e){
11461         if(e.button == 0){
11462             this.el.removeClass("x-btn-click");
11463             Roo.get(document).un('mouseup', this.onMouseUp, this);
11464         }
11465     },
11466     // private
11467     onMenuShow : function(e){
11468         this.el.addClass("x-btn-menu-active");
11469     },
11470     // private
11471     onMenuHide : function(e){
11472         this.el.removeClass("x-btn-menu-active");
11473     }   
11474 });
11475
11476 // Private utility class used by Button
11477 Roo.ButtonToggleMgr = function(){
11478    var groups = {};
11479    
11480    function toggleGroup(btn, state){
11481        if(state){
11482            var g = groups[btn.toggleGroup];
11483            for(var i = 0, l = g.length; i < l; i++){
11484                if(g[i] != btn){
11485                    g[i].toggle(false);
11486                }
11487            }
11488        }
11489    }
11490    
11491    return {
11492        register : function(btn){
11493            if(!btn.toggleGroup){
11494                return;
11495            }
11496            var g = groups[btn.toggleGroup];
11497            if(!g){
11498                g = groups[btn.toggleGroup] = [];
11499            }
11500            g.push(btn);
11501            btn.on("toggle", toggleGroup);
11502        },
11503        
11504        unregister : function(btn){
11505            if(!btn.toggleGroup){
11506                return;
11507            }
11508            var g = groups[btn.toggleGroup];
11509            if(g){
11510                g.remove(btn);
11511                btn.un("toggle", toggleGroup);
11512            }
11513        }
11514    };
11515 }();/*
11516  * Based on:
11517  * Ext JS Library 1.1.1
11518  * Copyright(c) 2006-2007, Ext JS, LLC.
11519  *
11520  * Originally Released Under LGPL - original licence link has changed is not relivant.
11521  *
11522  * Fork - LGPL
11523  * <script type="text/javascript">
11524  */
11525  
11526 /**
11527  * @class Roo.SplitButton
11528  * @extends Roo.Button
11529  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11530  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11531  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11532  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11533  * @cfg {String} arrowTooltip The title attribute of the arrow
11534  * @constructor
11535  * Create a new menu button
11536  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11537  * @param {Object} config The config object
11538  */
11539 Roo.SplitButton = function(renderTo, config){
11540     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11541     /**
11542      * @event arrowclick
11543      * Fires when this button's arrow is clicked
11544      * @param {SplitButton} this
11545      * @param {EventObject} e The click event
11546      */
11547     this.addEvents({"arrowclick":true});
11548 };
11549
11550 Roo.extend(Roo.SplitButton, Roo.Button, {
11551     render : function(renderTo){
11552         // this is one sweet looking template!
11553         var tpl = new Roo.Template(
11554             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11555             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11556             '<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>',
11557             "</tbody></table></td><td>",
11558             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11559             '<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>',
11560             "</tbody></table></td></tr></table>"
11561         );
11562         var btn = tpl.append(renderTo, [this.text, this.type], true);
11563         var btnEl = btn.child("button");
11564         if(this.cls){
11565             btn.addClass(this.cls);
11566         }
11567         if(this.icon){
11568             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11569         }
11570         if(this.iconCls){
11571             btnEl.addClass(this.iconCls);
11572             if(!this.cls){
11573                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11574             }
11575         }
11576         this.el = btn;
11577         if(this.handleMouseEvents){
11578             btn.on("mouseover", this.onMouseOver, this);
11579             btn.on("mouseout", this.onMouseOut, this);
11580             btn.on("mousedown", this.onMouseDown, this);
11581             btn.on("mouseup", this.onMouseUp, this);
11582         }
11583         btn.on(this.clickEvent, this.onClick, this);
11584         if(this.tooltip){
11585             if(typeof this.tooltip == 'object'){
11586                 Roo.QuickTips.tips(Roo.apply({
11587                       target: btnEl.id
11588                 }, this.tooltip));
11589             } else {
11590                 btnEl.dom[this.tooltipType] = this.tooltip;
11591             }
11592         }
11593         if(this.arrowTooltip){
11594             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11595         }
11596         if(this.hidden){
11597             this.hide();
11598         }
11599         if(this.disabled){
11600             this.disable();
11601         }
11602         if(this.pressed){
11603             this.el.addClass("x-btn-pressed");
11604         }
11605         if(Roo.isIE && !Roo.isIE7){
11606             this.autoWidth.defer(1, this);
11607         }else{
11608             this.autoWidth();
11609         }
11610         if(this.menu){
11611             this.menu.on("show", this.onMenuShow, this);
11612             this.menu.on("hide", this.onMenuHide, this);
11613         }
11614         this.fireEvent('render', this);
11615     },
11616
11617     // private
11618     autoWidth : function(){
11619         if(this.el){
11620             var tbl = this.el.child("table:first");
11621             var tbl2 = this.el.child("table:last");
11622             this.el.setWidth("auto");
11623             tbl.setWidth("auto");
11624             if(Roo.isIE7 && Roo.isStrict){
11625                 var ib = this.el.child('button:first');
11626                 if(ib && ib.getWidth() > 20){
11627                     ib.clip();
11628                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11629                 }
11630             }
11631             if(this.minWidth){
11632                 if(this.hidden){
11633                     this.el.beginMeasure();
11634                 }
11635                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11636                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11637                 }
11638                 if(this.hidden){
11639                     this.el.endMeasure();
11640                 }
11641             }
11642             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11643         } 
11644     },
11645     /**
11646      * Sets this button's click handler
11647      * @param {Function} handler The function to call when the button is clicked
11648      * @param {Object} scope (optional) Scope for the function passed above
11649      */
11650     setHandler : function(handler, scope){
11651         this.handler = handler;
11652         this.scope = scope;  
11653     },
11654     
11655     /**
11656      * Sets this button's arrow click handler
11657      * @param {Function} handler The function to call when the arrow is clicked
11658      * @param {Object} scope (optional) Scope for the function passed above
11659      */
11660     setArrowHandler : function(handler, scope){
11661         this.arrowHandler = handler;
11662         this.scope = scope;  
11663     },
11664     
11665     /**
11666      * Focus the button
11667      */
11668     focus : function(){
11669         if(this.el){
11670             this.el.child("button:first").focus();
11671         }
11672     },
11673
11674     // private
11675     onClick : function(e){
11676         e.preventDefault();
11677         if(!this.disabled){
11678             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11679                 if(this.menu && !this.menu.isVisible()){
11680                     this.menu.show(this.el, this.menuAlign);
11681                 }
11682                 this.fireEvent("arrowclick", this, e);
11683                 if(this.arrowHandler){
11684                     this.arrowHandler.call(this.scope || this, this, e);
11685                 }
11686             }else{
11687                 this.fireEvent("click", this, e);
11688                 if(this.handler){
11689                     this.handler.call(this.scope || this, this, e);
11690                 }
11691             }
11692         }
11693     },
11694     // private
11695     onMouseDown : function(e){
11696         if(!this.disabled){
11697             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11698         }
11699     },
11700     // private
11701     onMouseUp : function(e){
11702         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11703     }   
11704 });
11705
11706
11707 // backwards compat
11708 Roo.MenuButton = Roo.SplitButton;/*
11709  * Based on:
11710  * Ext JS Library 1.1.1
11711  * Copyright(c) 2006-2007, Ext JS, LLC.
11712  *
11713  * Originally Released Under LGPL - original licence link has changed is not relivant.
11714  *
11715  * Fork - LGPL
11716  * <script type="text/javascript">
11717  */
11718
11719 /**
11720  * @class Roo.Toolbar
11721  * Basic Toolbar class.
11722  * @constructor
11723  * Creates a new Toolbar
11724  * @param {Object} container The config object
11725  */ 
11726 Roo.Toolbar = function(container, buttons, config)
11727 {
11728     /// old consturctor format still supported..
11729     if(container instanceof Array){ // omit the container for later rendering
11730         buttons = container;
11731         config = buttons;
11732         container = null;
11733     }
11734     if (typeof(container) == 'object' && container.xtype) {
11735         config = container;
11736         container = config.container;
11737         buttons = config.buttons || []; // not really - use items!!
11738     }
11739     var xitems = [];
11740     if (config && config.items) {
11741         xitems = config.items;
11742         delete config.items;
11743     }
11744     Roo.apply(this, config);
11745     this.buttons = buttons;
11746     
11747     if(container){
11748         this.render(container);
11749     }
11750     this.xitems = xitems;
11751     Roo.each(xitems, function(b) {
11752         this.add(b);
11753     }, this);
11754     
11755 };
11756
11757 Roo.Toolbar.prototype = {
11758     /**
11759      * @cfg {Array} items
11760      * array of button configs or elements to add (will be converted to a MixedCollection)
11761      */
11762     
11763     /**
11764      * @cfg {String/HTMLElement/Element} container
11765      * The id or element that will contain the toolbar
11766      */
11767     // private
11768     render : function(ct){
11769         this.el = Roo.get(ct);
11770         if(this.cls){
11771             this.el.addClass(this.cls);
11772         }
11773         // using a table allows for vertical alignment
11774         // 100% width is needed by Safari...
11775         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11776         this.tr = this.el.child("tr", true);
11777         var autoId = 0;
11778         this.items = new Roo.util.MixedCollection(false, function(o){
11779             return o.id || ("item" + (++autoId));
11780         });
11781         if(this.buttons){
11782             this.add.apply(this, this.buttons);
11783             delete this.buttons;
11784         }
11785     },
11786
11787     /**
11788      * Adds element(s) to the toolbar -- this function takes a variable number of 
11789      * arguments of mixed type and adds them to the toolbar.
11790      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11791      * <ul>
11792      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11793      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11794      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11795      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11796      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11797      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11798      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11799      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11800      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11801      * </ul>
11802      * @param {Mixed} arg2
11803      * @param {Mixed} etc.
11804      */
11805     add : function(){
11806         var a = arguments, l = a.length;
11807         for(var i = 0; i < l; i++){
11808             this._add(a[i]);
11809         }
11810     },
11811     // private..
11812     _add : function(el) {
11813         
11814         if (el.xtype) {
11815             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11816         }
11817         
11818         if (el.applyTo){ // some kind of form field
11819             return this.addField(el);
11820         } 
11821         if (el.render){ // some kind of Toolbar.Item
11822             return this.addItem(el);
11823         }
11824         if (typeof el == "string"){ // string
11825             if(el == "separator" || el == "-"){
11826                 return this.addSeparator();
11827             }
11828             if (el == " "){
11829                 return this.addSpacer();
11830             }
11831             if(el == "->"){
11832                 return this.addFill();
11833             }
11834             return this.addText(el);
11835             
11836         }
11837         if(el.tagName){ // element
11838             return this.addElement(el);
11839         }
11840         if(typeof el == "object"){ // must be button config?
11841             return this.addButton(el);
11842         }
11843         // and now what?!?!
11844         return false;
11845         
11846     },
11847     
11848     /**
11849      * Add an Xtype element
11850      * @param {Object} xtype Xtype Object
11851      * @return {Object} created Object
11852      */
11853     addxtype : function(e){
11854         return this.add(e);  
11855     },
11856     
11857     /**
11858      * Returns the Element for this toolbar.
11859      * @return {Roo.Element}
11860      */
11861     getEl : function(){
11862         return this.el;  
11863     },
11864     
11865     /**
11866      * Adds a separator
11867      * @return {Roo.Toolbar.Item} The separator item
11868      */
11869     addSeparator : function(){
11870         return this.addItem(new Roo.Toolbar.Separator());
11871     },
11872
11873     /**
11874      * Adds a spacer element
11875      * @return {Roo.Toolbar.Spacer} The spacer item
11876      */
11877     addSpacer : function(){
11878         return this.addItem(new Roo.Toolbar.Spacer());
11879     },
11880
11881     /**
11882      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11883      * @return {Roo.Toolbar.Fill} The fill item
11884      */
11885     addFill : function(){
11886         return this.addItem(new Roo.Toolbar.Fill());
11887     },
11888
11889     /**
11890      * Adds any standard HTML element to the toolbar
11891      * @param {String/HTMLElement/Element} el The element or id of the element to add
11892      * @return {Roo.Toolbar.Item} The element's item
11893      */
11894     addElement : function(el){
11895         return this.addItem(new Roo.Toolbar.Item(el));
11896     },
11897     /**
11898      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11899      * @type Roo.util.MixedCollection  
11900      */
11901     items : false,
11902      
11903     /**
11904      * Adds any Toolbar.Item or subclass
11905      * @param {Roo.Toolbar.Item} item
11906      * @return {Roo.Toolbar.Item} The item
11907      */
11908     addItem : function(item){
11909         var td = this.nextBlock();
11910         item.render(td);
11911         this.items.add(item);
11912         return item;
11913     },
11914     
11915     /**
11916      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11917      * @param {Object/Array} config A button config or array of configs
11918      * @return {Roo.Toolbar.Button/Array}
11919      */
11920     addButton : function(config){
11921         if(config instanceof Array){
11922             var buttons = [];
11923             for(var i = 0, len = config.length; i < len; i++) {
11924                 buttons.push(this.addButton(config[i]));
11925             }
11926             return buttons;
11927         }
11928         var b = config;
11929         if(!(config instanceof Roo.Toolbar.Button)){
11930             b = config.split ?
11931                 new Roo.Toolbar.SplitButton(config) :
11932                 new Roo.Toolbar.Button(config);
11933         }
11934         var td = this.nextBlock();
11935         b.render(td);
11936         this.items.add(b);
11937         return b;
11938     },
11939     
11940     /**
11941      * Adds text to the toolbar
11942      * @param {String} text The text to add
11943      * @return {Roo.Toolbar.Item} The element's item
11944      */
11945     addText : function(text){
11946         return this.addItem(new Roo.Toolbar.TextItem(text));
11947     },
11948     
11949     /**
11950      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11951      * @param {Number} index The index where the item is to be inserted
11952      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11953      * @return {Roo.Toolbar.Button/Item}
11954      */
11955     insertButton : function(index, item){
11956         if(item instanceof Array){
11957             var buttons = [];
11958             for(var i = 0, len = item.length; i < len; i++) {
11959                buttons.push(this.insertButton(index + i, item[i]));
11960             }
11961             return buttons;
11962         }
11963         if (!(item instanceof Roo.Toolbar.Button)){
11964            item = new Roo.Toolbar.Button(item);
11965         }
11966         var td = document.createElement("td");
11967         this.tr.insertBefore(td, this.tr.childNodes[index]);
11968         item.render(td);
11969         this.items.insert(index, item);
11970         return item;
11971     },
11972     
11973     /**
11974      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
11975      * @param {Object} config
11976      * @return {Roo.Toolbar.Item} The element's item
11977      */
11978     addDom : function(config, returnEl){
11979         var td = this.nextBlock();
11980         Roo.DomHelper.overwrite(td, config);
11981         var ti = new Roo.Toolbar.Item(td.firstChild);
11982         ti.render(td);
11983         this.items.add(ti);
11984         return ti;
11985     },
11986
11987     /**
11988      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
11989      * @type Roo.util.MixedCollection  
11990      */
11991     fields : false,
11992     
11993     /**
11994      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
11995      * Note: the field should not have been rendered yet. For a field that has already been
11996      * rendered, use {@link #addElement}.
11997      * @param {Roo.form.Field} field
11998      * @return {Roo.ToolbarItem}
11999      */
12000      
12001       
12002     addField : function(field) {
12003         if (!this.fields) {
12004             var autoId = 0;
12005             this.fields = new Roo.util.MixedCollection(false, function(o){
12006                 return o.id || ("item" + (++autoId));
12007             });
12008
12009         }
12010         
12011         var td = this.nextBlock();
12012         field.render(td);
12013         var ti = new Roo.Toolbar.Item(td.firstChild);
12014         ti.render(td);
12015         this.items.add(ti);
12016         this.fields.add(field);
12017         return ti;
12018     },
12019     /**
12020      * Hide the toolbar
12021      * @method hide
12022      */
12023      
12024       
12025     hide : function()
12026     {
12027         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12028         this.el.child('div').hide();
12029     },
12030     /**
12031      * Show the toolbar
12032      * @method show
12033      */
12034     show : function()
12035     {
12036         this.el.child('div').show();
12037     },
12038       
12039     // private
12040     nextBlock : function(){
12041         var td = document.createElement("td");
12042         this.tr.appendChild(td);
12043         return td;
12044     },
12045
12046     // private
12047     destroy : function(){
12048         if(this.items){ // rendered?
12049             Roo.destroy.apply(Roo, this.items.items);
12050         }
12051         if(this.fields){ // rendered?
12052             Roo.destroy.apply(Roo, this.fields.items);
12053         }
12054         Roo.Element.uncache(this.el, this.tr);
12055     }
12056 };
12057
12058 /**
12059  * @class Roo.Toolbar.Item
12060  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12061  * @constructor
12062  * Creates a new Item
12063  * @param {HTMLElement} el 
12064  */
12065 Roo.Toolbar.Item = function(el){
12066     this.el = Roo.getDom(el);
12067     this.id = Roo.id(this.el);
12068     this.hidden = false;
12069 };
12070
12071 Roo.Toolbar.Item.prototype = {
12072     
12073     /**
12074      * Get this item's HTML Element
12075      * @return {HTMLElement}
12076      */
12077     getEl : function(){
12078        return this.el;  
12079     },
12080
12081     // private
12082     render : function(td){
12083         this.td = td;
12084         td.appendChild(this.el);
12085     },
12086     
12087     /**
12088      * Removes and destroys this item.
12089      */
12090     destroy : function(){
12091         this.td.parentNode.removeChild(this.td);
12092     },
12093     
12094     /**
12095      * Shows this item.
12096      */
12097     show: function(){
12098         this.hidden = false;
12099         this.td.style.display = "";
12100     },
12101     
12102     /**
12103      * Hides this item.
12104      */
12105     hide: function(){
12106         this.hidden = true;
12107         this.td.style.display = "none";
12108     },
12109     
12110     /**
12111      * Convenience function for boolean show/hide.
12112      * @param {Boolean} visible true to show/false to hide
12113      */
12114     setVisible: function(visible){
12115         if(visible) {
12116             this.show();
12117         }else{
12118             this.hide();
12119         }
12120     },
12121     
12122     /**
12123      * Try to focus this item.
12124      */
12125     focus : function(){
12126         Roo.fly(this.el).focus();
12127     },
12128     
12129     /**
12130      * Disables this item.
12131      */
12132     disable : function(){
12133         Roo.fly(this.td).addClass("x-item-disabled");
12134         this.disabled = true;
12135         this.el.disabled = true;
12136     },
12137     
12138     /**
12139      * Enables this item.
12140      */
12141     enable : function(){
12142         Roo.fly(this.td).removeClass("x-item-disabled");
12143         this.disabled = false;
12144         this.el.disabled = false;
12145     }
12146 };
12147
12148
12149 /**
12150  * @class Roo.Toolbar.Separator
12151  * @extends Roo.Toolbar.Item
12152  * A simple toolbar separator class
12153  * @constructor
12154  * Creates a new Separator
12155  */
12156 Roo.Toolbar.Separator = function(){
12157     var s = document.createElement("span");
12158     s.className = "ytb-sep";
12159     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12160 };
12161 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12162     enable:Roo.emptyFn,
12163     disable:Roo.emptyFn,
12164     focus:Roo.emptyFn
12165 });
12166
12167 /**
12168  * @class Roo.Toolbar.Spacer
12169  * @extends Roo.Toolbar.Item
12170  * A simple element that adds extra horizontal space to a toolbar.
12171  * @constructor
12172  * Creates a new Spacer
12173  */
12174 Roo.Toolbar.Spacer = function(){
12175     var s = document.createElement("div");
12176     s.className = "ytb-spacer";
12177     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12178 };
12179 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12180     enable:Roo.emptyFn,
12181     disable:Roo.emptyFn,
12182     focus:Roo.emptyFn
12183 });
12184
12185 /**
12186  * @class Roo.Toolbar.Fill
12187  * @extends Roo.Toolbar.Spacer
12188  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12189  * @constructor
12190  * Creates a new Spacer
12191  */
12192 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12193     // private
12194     render : function(td){
12195         td.style.width = '100%';
12196         Roo.Toolbar.Fill.superclass.render.call(this, td);
12197     }
12198 });
12199
12200 /**
12201  * @class Roo.Toolbar.TextItem
12202  * @extends Roo.Toolbar.Item
12203  * A simple class that renders text directly into a toolbar.
12204  * @constructor
12205  * Creates a new TextItem
12206  * @param {String} text
12207  */
12208 Roo.Toolbar.TextItem = function(text){
12209     if (typeof(text) == 'object') {
12210         text = text.text;
12211     }
12212     var s = document.createElement("span");
12213     s.className = "ytb-text";
12214     s.innerHTML = text;
12215     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12216 };
12217 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12218     enable:Roo.emptyFn,
12219     disable:Roo.emptyFn,
12220     focus:Roo.emptyFn
12221 });
12222
12223 /**
12224  * @class Roo.Toolbar.Button
12225  * @extends Roo.Button
12226  * A button that renders into a toolbar.
12227  * @constructor
12228  * Creates a new Button
12229  * @param {Object} config A standard {@link Roo.Button} config object
12230  */
12231 Roo.Toolbar.Button = function(config){
12232     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12233 };
12234 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12235     render : function(td){
12236         this.td = td;
12237         Roo.Toolbar.Button.superclass.render.call(this, td);
12238     },
12239     
12240     /**
12241      * Removes and destroys this button
12242      */
12243     destroy : function(){
12244         Roo.Toolbar.Button.superclass.destroy.call(this);
12245         this.td.parentNode.removeChild(this.td);
12246     },
12247     
12248     /**
12249      * Shows this button
12250      */
12251     show: function(){
12252         this.hidden = false;
12253         this.td.style.display = "";
12254     },
12255     
12256     /**
12257      * Hides this button
12258      */
12259     hide: function(){
12260         this.hidden = true;
12261         this.td.style.display = "none";
12262     },
12263
12264     /**
12265      * Disables this item
12266      */
12267     disable : function(){
12268         Roo.fly(this.td).addClass("x-item-disabled");
12269         this.disabled = true;
12270     },
12271
12272     /**
12273      * Enables this item
12274      */
12275     enable : function(){
12276         Roo.fly(this.td).removeClass("x-item-disabled");
12277         this.disabled = false;
12278     }
12279 });
12280 // backwards compat
12281 Roo.ToolbarButton = Roo.Toolbar.Button;
12282
12283 /**
12284  * @class Roo.Toolbar.SplitButton
12285  * @extends Roo.SplitButton
12286  * A menu button that renders into a toolbar.
12287  * @constructor
12288  * Creates a new SplitButton
12289  * @param {Object} config A standard {@link Roo.SplitButton} config object
12290  */
12291 Roo.Toolbar.SplitButton = function(config){
12292     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12293 };
12294 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12295     render : function(td){
12296         this.td = td;
12297         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12298     },
12299     
12300     /**
12301      * Removes and destroys this button
12302      */
12303     destroy : function(){
12304         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12305         this.td.parentNode.removeChild(this.td);
12306     },
12307     
12308     /**
12309      * Shows this button
12310      */
12311     show: function(){
12312         this.hidden = false;
12313         this.td.style.display = "";
12314     },
12315     
12316     /**
12317      * Hides this button
12318      */
12319     hide: function(){
12320         this.hidden = true;
12321         this.td.style.display = "none";
12322     }
12323 });
12324
12325 // backwards compat
12326 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12327  * Based on:
12328  * Ext JS Library 1.1.1
12329  * Copyright(c) 2006-2007, Ext JS, LLC.
12330  *
12331  * Originally Released Under LGPL - original licence link has changed is not relivant.
12332  *
12333  * Fork - LGPL
12334  * <script type="text/javascript">
12335  */
12336  
12337 /**
12338  * @class Roo.PagingToolbar
12339  * @extends Roo.Toolbar
12340  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12341  * @constructor
12342  * Create a new PagingToolbar
12343  * @param {Object} config The config object
12344  */
12345 Roo.PagingToolbar = function(el, ds, config)
12346 {
12347     // old args format still supported... - xtype is prefered..
12348     if (typeof(el) == 'object' && el.xtype) {
12349         // created from xtype...
12350         config = el;
12351         ds = el.dataSource;
12352         el = config.container;
12353     }
12354     var items = [];
12355     if (config.items) {
12356         items = config.items;
12357         config.items = [];
12358     }
12359     
12360     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12361     this.ds = ds;
12362     this.cursor = 0;
12363     this.renderButtons(this.el);
12364     this.bind(ds);
12365     
12366     // supprot items array.
12367    
12368     Roo.each(items, function(e) {
12369         this.add(Roo.factory(e));
12370     },this);
12371     
12372 };
12373
12374 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12375     /**
12376      * @cfg {Roo.data.Store} dataSource
12377      * The underlying data store providing the paged data
12378      */
12379     /**
12380      * @cfg {String/HTMLElement/Element} container
12381      * container The id or element that will contain the toolbar
12382      */
12383     /**
12384      * @cfg {Boolean} displayInfo
12385      * True to display the displayMsg (defaults to false)
12386      */
12387     /**
12388      * @cfg {Number} pageSize
12389      * The number of records to display per page (defaults to 20)
12390      */
12391     pageSize: 20,
12392     /**
12393      * @cfg {String} displayMsg
12394      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12395      */
12396     displayMsg : 'Displaying {0} - {1} of {2}',
12397     /**
12398      * @cfg {String} emptyMsg
12399      * The message to display when no records are found (defaults to "No data to display")
12400      */
12401     emptyMsg : 'No data to display',
12402     /**
12403      * Customizable piece of the default paging text (defaults to "Page")
12404      * @type String
12405      */
12406     beforePageText : "Page",
12407     /**
12408      * Customizable piece of the default paging text (defaults to "of %0")
12409      * @type String
12410      */
12411     afterPageText : "of {0}",
12412     /**
12413      * Customizable piece of the default paging text (defaults to "First Page")
12414      * @type String
12415      */
12416     firstText : "First Page",
12417     /**
12418      * Customizable piece of the default paging text (defaults to "Previous Page")
12419      * @type String
12420      */
12421     prevText : "Previous Page",
12422     /**
12423      * Customizable piece of the default paging text (defaults to "Next Page")
12424      * @type String
12425      */
12426     nextText : "Next Page",
12427     /**
12428      * Customizable piece of the default paging text (defaults to "Last Page")
12429      * @type String
12430      */
12431     lastText : "Last Page",
12432     /**
12433      * Customizable piece of the default paging text (defaults to "Refresh")
12434      * @type String
12435      */
12436     refreshText : "Refresh",
12437
12438     // private
12439     renderButtons : function(el){
12440         Roo.PagingToolbar.superclass.render.call(this, el);
12441         this.first = this.addButton({
12442             tooltip: this.firstText,
12443             cls: "x-btn-icon x-grid-page-first",
12444             disabled: true,
12445             handler: this.onClick.createDelegate(this, ["first"])
12446         });
12447         this.prev = this.addButton({
12448             tooltip: this.prevText,
12449             cls: "x-btn-icon x-grid-page-prev",
12450             disabled: true,
12451             handler: this.onClick.createDelegate(this, ["prev"])
12452         });
12453         //this.addSeparator();
12454         this.add(this.beforePageText);
12455         this.field = Roo.get(this.addDom({
12456            tag: "input",
12457            type: "text",
12458            size: "3",
12459            value: "1",
12460            cls: "x-grid-page-number"
12461         }).el);
12462         this.field.on("keydown", this.onPagingKeydown, this);
12463         this.field.on("focus", function(){this.dom.select();});
12464         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12465         this.field.setHeight(18);
12466         //this.addSeparator();
12467         this.next = this.addButton({
12468             tooltip: this.nextText,
12469             cls: "x-btn-icon x-grid-page-next",
12470             disabled: true,
12471             handler: this.onClick.createDelegate(this, ["next"])
12472         });
12473         this.last = this.addButton({
12474             tooltip: this.lastText,
12475             cls: "x-btn-icon x-grid-page-last",
12476             disabled: true,
12477             handler: this.onClick.createDelegate(this, ["last"])
12478         });
12479         //this.addSeparator();
12480         this.loading = this.addButton({
12481             tooltip: this.refreshText,
12482             cls: "x-btn-icon x-grid-loading",
12483             handler: this.onClick.createDelegate(this, ["refresh"])
12484         });
12485
12486         if(this.displayInfo){
12487             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12488         }
12489     },
12490
12491     // private
12492     updateInfo : function(){
12493         if(this.displayEl){
12494             var count = this.ds.getCount();
12495             var msg = count == 0 ?
12496                 this.emptyMsg :
12497                 String.format(
12498                     this.displayMsg,
12499                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12500                 );
12501             this.displayEl.update(msg);
12502         }
12503     },
12504
12505     // private
12506     onLoad : function(ds, r, o){
12507        this.cursor = o.params ? o.params.start : 0;
12508        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12509
12510        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12511        this.field.dom.value = ap;
12512        this.first.setDisabled(ap == 1);
12513        this.prev.setDisabled(ap == 1);
12514        this.next.setDisabled(ap == ps);
12515        this.last.setDisabled(ap == ps);
12516        this.loading.enable();
12517        this.updateInfo();
12518     },
12519
12520     // private
12521     getPageData : function(){
12522         var total = this.ds.getTotalCount();
12523         return {
12524             total : total,
12525             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12526             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12527         };
12528     },
12529
12530     // private
12531     onLoadError : function(){
12532         this.loading.enable();
12533     },
12534
12535     // private
12536     onPagingKeydown : function(e){
12537         var k = e.getKey();
12538         var d = this.getPageData();
12539         if(k == e.RETURN){
12540             var v = this.field.dom.value, pageNum;
12541             if(!v || isNaN(pageNum = parseInt(v, 10))){
12542                 this.field.dom.value = d.activePage;
12543                 return;
12544             }
12545             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12546             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12547             e.stopEvent();
12548         }
12549         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))
12550         {
12551           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12552           this.field.dom.value = pageNum;
12553           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12554           e.stopEvent();
12555         }
12556         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12557         {
12558           var v = this.field.dom.value, pageNum; 
12559           var increment = (e.shiftKey) ? 10 : 1;
12560           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12561             increment *= -1;
12562           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12563             this.field.dom.value = d.activePage;
12564             return;
12565           }
12566           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12567           {
12568             this.field.dom.value = parseInt(v, 10) + increment;
12569             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12570             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12571           }
12572           e.stopEvent();
12573         }
12574     },
12575
12576     // private
12577     beforeLoad : function(){
12578         if(this.loading){
12579             this.loading.disable();
12580         }
12581     },
12582
12583     // private
12584     onClick : function(which){
12585         var ds = this.ds;
12586         switch(which){
12587             case "first":
12588                 ds.load({params:{start: 0, limit: this.pageSize}});
12589             break;
12590             case "prev":
12591                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12592             break;
12593             case "next":
12594                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12595             break;
12596             case "last":
12597                 var total = ds.getTotalCount();
12598                 var extra = total % this.pageSize;
12599                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12600                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12601             break;
12602             case "refresh":
12603                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12604             break;
12605         }
12606     },
12607
12608     /**
12609      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12610      * @param {Roo.data.Store} store The data store to unbind
12611      */
12612     unbind : function(ds){
12613         ds.un("beforeload", this.beforeLoad, this);
12614         ds.un("load", this.onLoad, this);
12615         ds.un("loadexception", this.onLoadError, this);
12616         ds.un("remove", this.updateInfo, this);
12617         ds.un("add", this.updateInfo, this);
12618         this.ds = undefined;
12619     },
12620
12621     /**
12622      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12623      * @param {Roo.data.Store} store The data store to bind
12624      */
12625     bind : function(ds){
12626         ds.on("beforeload", this.beforeLoad, this);
12627         ds.on("load", this.onLoad, this);
12628         ds.on("loadexception", this.onLoadError, this);
12629         ds.on("remove", this.updateInfo, this);
12630         ds.on("add", this.updateInfo, this);
12631         this.ds = ds;
12632     }
12633 });/*
12634  * Based on:
12635  * Ext JS Library 1.1.1
12636  * Copyright(c) 2006-2007, Ext JS, LLC.
12637  *
12638  * Originally Released Under LGPL - original licence link has changed is not relivant.
12639  *
12640  * Fork - LGPL
12641  * <script type="text/javascript">
12642  */
12643
12644 /**
12645  * @class Roo.Resizable
12646  * @extends Roo.util.Observable
12647  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12648  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12649  * 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
12650  * the element will be wrapped for you automatically.</p>
12651  * <p>Here is the list of valid resize handles:</p>
12652  * <pre>
12653 Value   Description
12654 ------  -------------------
12655  'n'     north
12656  's'     south
12657  'e'     east
12658  'w'     west
12659  'nw'    northwest
12660  'sw'    southwest
12661  'se'    southeast
12662  'ne'    northeast
12663  'hd'    horizontal drag
12664  'all'   all
12665 </pre>
12666  * <p>Here's an example showing the creation of a typical Resizable:</p>
12667  * <pre><code>
12668 var resizer = new Roo.Resizable("element-id", {
12669     handles: 'all',
12670     minWidth: 200,
12671     minHeight: 100,
12672     maxWidth: 500,
12673     maxHeight: 400,
12674     pinned: true
12675 });
12676 resizer.on("resize", myHandler);
12677 </code></pre>
12678  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12679  * resizer.east.setDisplayed(false);</p>
12680  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12681  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12682  * resize operation's new size (defaults to [0, 0])
12683  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12684  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12685  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12686  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12687  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12688  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12689  * @cfg {Number} width The width of the element in pixels (defaults to null)
12690  * @cfg {Number} height The height of the element in pixels (defaults to null)
12691  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12692  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12693  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12694  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12695  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12696  * in favor of the handles config option (defaults to false)
12697  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12698  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12699  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12700  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12701  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12702  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12703  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12704  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12705  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12706  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12707  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12708  * @constructor
12709  * Create a new resizable component
12710  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12711  * @param {Object} config configuration options
12712   */
12713 Roo.Resizable = function(el, config)
12714 {
12715     this.el = Roo.get(el);
12716
12717     if(config && config.wrap){
12718         config.resizeChild = this.el;
12719         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12720         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12721         this.el.setStyle("overflow", "hidden");
12722         this.el.setPositioning(config.resizeChild.getPositioning());
12723         config.resizeChild.clearPositioning();
12724         if(!config.width || !config.height){
12725             var csize = config.resizeChild.getSize();
12726             this.el.setSize(csize.width, csize.height);
12727         }
12728         if(config.pinned && !config.adjustments){
12729             config.adjustments = "auto";
12730         }
12731     }
12732
12733     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12734     this.proxy.unselectable();
12735     this.proxy.enableDisplayMode('block');
12736
12737     Roo.apply(this, config);
12738
12739     if(this.pinned){
12740         this.disableTrackOver = true;
12741         this.el.addClass("x-resizable-pinned");
12742     }
12743     // if the element isn't positioned, make it relative
12744     var position = this.el.getStyle("position");
12745     if(position != "absolute" && position != "fixed"){
12746         this.el.setStyle("position", "relative");
12747     }
12748     if(!this.handles){ // no handles passed, must be legacy style
12749         this.handles = 's,e,se';
12750         if(this.multiDirectional){
12751             this.handles += ',n,w';
12752         }
12753     }
12754     if(this.handles == "all"){
12755         this.handles = "n s e w ne nw se sw";
12756     }
12757     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12758     var ps = Roo.Resizable.positions;
12759     for(var i = 0, len = hs.length; i < len; i++){
12760         if(hs[i] && ps[hs[i]]){
12761             var pos = ps[hs[i]];
12762             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12763         }
12764     }
12765     // legacy
12766     this.corner = this.southeast;
12767     
12768     // updateBox = the box can move..
12769     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12770         this.updateBox = true;
12771     }
12772
12773     this.activeHandle = null;
12774
12775     if(this.resizeChild){
12776         if(typeof this.resizeChild == "boolean"){
12777             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12778         }else{
12779             this.resizeChild = Roo.get(this.resizeChild, true);
12780         }
12781     }
12782     
12783     if(this.adjustments == "auto"){
12784         var rc = this.resizeChild;
12785         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12786         if(rc && (hw || hn)){
12787             rc.position("relative");
12788             rc.setLeft(hw ? hw.el.getWidth() : 0);
12789             rc.setTop(hn ? hn.el.getHeight() : 0);
12790         }
12791         this.adjustments = [
12792             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12793             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12794         ];
12795     }
12796
12797     if(this.draggable){
12798         this.dd = this.dynamic ?
12799             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12800         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12801     }
12802
12803     // public events
12804     this.addEvents({
12805         /**
12806          * @event beforeresize
12807          * Fired before resize is allowed. Set enabled to false to cancel resize.
12808          * @param {Roo.Resizable} this
12809          * @param {Roo.EventObject} e The mousedown event
12810          */
12811         "beforeresize" : true,
12812         /**
12813          * @event resizing
12814          * Fired a resizing.
12815          * @param {Roo.Resizable} this
12816          * @param {Number} x The new x position
12817          * @param {Number} y The new y position
12818          * @param {Number} w The new w width
12819          * @param {Number} h The new h hight
12820          * @param {Roo.EventObject} e The mouseup event
12821          */
12822         "resizing" : true,
12823         /**
12824          * @event resize
12825          * Fired after a resize.
12826          * @param {Roo.Resizable} this
12827          * @param {Number} width The new width
12828          * @param {Number} height The new height
12829          * @param {Roo.EventObject} e The mouseup event
12830          */
12831         "resize" : true
12832     });
12833
12834     if(this.width !== null && this.height !== null){
12835         this.resizeTo(this.width, this.height);
12836     }else{
12837         this.updateChildSize();
12838     }
12839     if(Roo.isIE){
12840         this.el.dom.style.zoom = 1;
12841     }
12842     Roo.Resizable.superclass.constructor.call(this);
12843 };
12844
12845 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12846         resizeChild : false,
12847         adjustments : [0, 0],
12848         minWidth : 5,
12849         minHeight : 5,
12850         maxWidth : 10000,
12851         maxHeight : 10000,
12852         enabled : true,
12853         animate : false,
12854         duration : .35,
12855         dynamic : false,
12856         handles : false,
12857         multiDirectional : false,
12858         disableTrackOver : false,
12859         easing : 'easeOutStrong',
12860         widthIncrement : 0,
12861         heightIncrement : 0,
12862         pinned : false,
12863         width : null,
12864         height : null,
12865         preserveRatio : false,
12866         transparent: false,
12867         minX: 0,
12868         minY: 0,
12869         draggable: false,
12870
12871         /**
12872          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12873          */
12874         constrainTo: undefined,
12875         /**
12876          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12877          */
12878         resizeRegion: undefined,
12879
12880
12881     /**
12882      * Perform a manual resize
12883      * @param {Number} width
12884      * @param {Number} height
12885      */
12886     resizeTo : function(width, height){
12887         this.el.setSize(width, height);
12888         this.updateChildSize();
12889         this.fireEvent("resize", this, width, height, null);
12890     },
12891
12892     // private
12893     startSizing : function(e, handle){
12894         this.fireEvent("beforeresize", this, e);
12895         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12896
12897             if(!this.overlay){
12898                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12899                 this.overlay.unselectable();
12900                 this.overlay.enableDisplayMode("block");
12901                 this.overlay.on("mousemove", this.onMouseMove, this);
12902                 this.overlay.on("mouseup", this.onMouseUp, this);
12903             }
12904             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12905
12906             this.resizing = true;
12907             this.startBox = this.el.getBox();
12908             this.startPoint = e.getXY();
12909             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12910                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12911
12912             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12913             this.overlay.show();
12914
12915             if(this.constrainTo) {
12916                 var ct = Roo.get(this.constrainTo);
12917                 this.resizeRegion = ct.getRegion().adjust(
12918                     ct.getFrameWidth('t'),
12919                     ct.getFrameWidth('l'),
12920                     -ct.getFrameWidth('b'),
12921                     -ct.getFrameWidth('r')
12922                 );
12923             }
12924
12925             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
12926             this.proxy.show();
12927             this.proxy.setBox(this.startBox);
12928             if(!this.dynamic){
12929                 this.proxy.setStyle('visibility', 'visible');
12930             }
12931         }
12932     },
12933
12934     // private
12935     onMouseDown : function(handle, e){
12936         if(this.enabled){
12937             e.stopEvent();
12938             this.activeHandle = handle;
12939             this.startSizing(e, handle);
12940         }
12941     },
12942
12943     // private
12944     onMouseUp : function(e){
12945         var size = this.resizeElement();
12946         this.resizing = false;
12947         this.handleOut();
12948         this.overlay.hide();
12949         this.proxy.hide();
12950         this.fireEvent("resize", this, size.width, size.height, e);
12951     },
12952
12953     // private
12954     updateChildSize : function(){
12955         
12956         if(this.resizeChild){
12957             var el = this.el;
12958             var child = this.resizeChild;
12959             var adj = this.adjustments;
12960             if(el.dom.offsetWidth){
12961                 var b = el.getSize(true);
12962                 child.setSize(b.width+adj[0], b.height+adj[1]);
12963             }
12964             // Second call here for IE
12965             // The first call enables instant resizing and
12966             // the second call corrects scroll bars if they
12967             // exist
12968             if(Roo.isIE){
12969                 setTimeout(function(){
12970                     if(el.dom.offsetWidth){
12971                         var b = el.getSize(true);
12972                         child.setSize(b.width+adj[0], b.height+adj[1]);
12973                     }
12974                 }, 10);
12975             }
12976         }
12977     },
12978
12979     // private
12980     snap : function(value, inc, min){
12981         if(!inc || !value) return value;
12982         var newValue = value;
12983         var m = value % inc;
12984         if(m > 0){
12985             if(m > (inc/2)){
12986                 newValue = value + (inc-m);
12987             }else{
12988                 newValue = value - m;
12989             }
12990         }
12991         return Math.max(min, newValue);
12992     },
12993
12994     // private
12995     resizeElement : function(){
12996         var box = this.proxy.getBox();
12997         if(this.updateBox){
12998             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
12999         }else{
13000             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13001         }
13002         this.updateChildSize();
13003         if(!this.dynamic){
13004             this.proxy.hide();
13005         }
13006         return box;
13007     },
13008
13009     // private
13010     constrain : function(v, diff, m, mx){
13011         if(v - diff < m){
13012             diff = v - m;
13013         }else if(v - diff > mx){
13014             diff = mx - v;
13015         }
13016         return diff;
13017     },
13018
13019     // private
13020     onMouseMove : function(e){
13021         
13022         if(this.enabled){
13023             try{// try catch so if something goes wrong the user doesn't get hung
13024
13025             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13026                 return;
13027             }
13028
13029             //var curXY = this.startPoint;
13030             var curSize = this.curSize || this.startBox;
13031             var x = this.startBox.x, y = this.startBox.y;
13032             var ox = x, oy = y;
13033             var w = curSize.width, h = curSize.height;
13034             var ow = w, oh = h;
13035             var mw = this.minWidth, mh = this.minHeight;
13036             var mxw = this.maxWidth, mxh = this.maxHeight;
13037             var wi = this.widthIncrement;
13038             var hi = this.heightIncrement;
13039
13040             var eventXY = e.getXY();
13041             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13042             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13043
13044             var pos = this.activeHandle.position;
13045
13046             switch(pos){
13047                 case "east":
13048                     w += diffX;
13049                     w = Math.min(Math.max(mw, w), mxw);
13050                     break;
13051              
13052                 case "south":
13053                     h += diffY;
13054                     h = Math.min(Math.max(mh, h), mxh);
13055                     break;
13056                 case "southeast":
13057                     w += diffX;
13058                     h += diffY;
13059                     w = Math.min(Math.max(mw, w), mxw);
13060                     h = Math.min(Math.max(mh, h), mxh);
13061                     break;
13062                 case "north":
13063                     diffY = this.constrain(h, diffY, mh, mxh);
13064                     y += diffY;
13065                     h -= diffY;
13066                     break;
13067                 case "hdrag":
13068                     
13069                     if (wi) {
13070                         var adiffX = Math.abs(diffX);
13071                         var sub = (adiffX % wi); // how much 
13072                         if (sub > (wi/2)) { // far enough to snap
13073                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13074                         } else {
13075                             // remove difference.. 
13076                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13077                         }
13078                     }
13079                     x += diffX;
13080                     x = Math.max(this.minX, x);
13081                     break;
13082                 case "west":
13083                     diffX = this.constrain(w, diffX, mw, mxw);
13084                     x += diffX;
13085                     w -= diffX;
13086                     break;
13087                 case "northeast":
13088                     w += diffX;
13089                     w = Math.min(Math.max(mw, w), mxw);
13090                     diffY = this.constrain(h, diffY, mh, mxh);
13091                     y += diffY;
13092                     h -= diffY;
13093                     break;
13094                 case "northwest":
13095                     diffX = this.constrain(w, diffX, mw, mxw);
13096                     diffY = this.constrain(h, diffY, mh, mxh);
13097                     y += diffY;
13098                     h -= diffY;
13099                     x += diffX;
13100                     w -= diffX;
13101                     break;
13102                case "southwest":
13103                     diffX = this.constrain(w, diffX, mw, mxw);
13104                     h += diffY;
13105                     h = Math.min(Math.max(mh, h), mxh);
13106                     x += diffX;
13107                     w -= diffX;
13108                     break;
13109             }
13110
13111             var sw = this.snap(w, wi, mw);
13112             var sh = this.snap(h, hi, mh);
13113             if(sw != w || sh != h){
13114                 switch(pos){
13115                     case "northeast":
13116                         y -= sh - h;
13117                     break;
13118                     case "north":
13119                         y -= sh - h;
13120                         break;
13121                     case "southwest":
13122                         x -= sw - w;
13123                     break;
13124                     case "west":
13125                         x -= sw - w;
13126                         break;
13127                     case "northwest":
13128                         x -= sw - w;
13129                         y -= sh - h;
13130                     break;
13131                 }
13132                 w = sw;
13133                 h = sh;
13134             }
13135
13136             if(this.preserveRatio){
13137                 switch(pos){
13138                     case "southeast":
13139                     case "east":
13140                         h = oh * (w/ow);
13141                         h = Math.min(Math.max(mh, h), mxh);
13142                         w = ow * (h/oh);
13143                        break;
13144                     case "south":
13145                         w = ow * (h/oh);
13146                         w = Math.min(Math.max(mw, w), mxw);
13147                         h = oh * (w/ow);
13148                         break;
13149                     case "northeast":
13150                         w = ow * (h/oh);
13151                         w = Math.min(Math.max(mw, w), mxw);
13152                         h = oh * (w/ow);
13153                     break;
13154                     case "north":
13155                         var tw = w;
13156                         w = ow * (h/oh);
13157                         w = Math.min(Math.max(mw, w), mxw);
13158                         h = oh * (w/ow);
13159                         x += (tw - w) / 2;
13160                         break;
13161                     case "southwest":
13162                         h = oh * (w/ow);
13163                         h = Math.min(Math.max(mh, h), mxh);
13164                         var tw = w;
13165                         w = ow * (h/oh);
13166                         x += tw - w;
13167                         break;
13168                     case "west":
13169                         var th = h;
13170                         h = oh * (w/ow);
13171                         h = Math.min(Math.max(mh, h), mxh);
13172                         y += (th - h) / 2;
13173                         var tw = w;
13174                         w = ow * (h/oh);
13175                         x += tw - w;
13176                        break;
13177                     case "northwest":
13178                         var tw = w;
13179                         var th = h;
13180                         h = oh * (w/ow);
13181                         h = Math.min(Math.max(mh, h), mxh);
13182                         w = ow * (h/oh);
13183                         y += th - h;
13184                         x += tw - w;
13185                        break;
13186
13187                 }
13188             }
13189             if (pos == 'hdrag') {
13190                 w = ow;
13191             }
13192             this.proxy.setBounds(x, y, w, h);
13193             if(this.dynamic){
13194                 this.resizeElement();
13195             }
13196             }catch(e){}
13197         }
13198         this.fireEvent("resizing", this, x, y, w, h, e);
13199     },
13200
13201     // private
13202     handleOver : function(){
13203         if(this.enabled){
13204             this.el.addClass("x-resizable-over");
13205         }
13206     },
13207
13208     // private
13209     handleOut : function(){
13210         if(!this.resizing){
13211             this.el.removeClass("x-resizable-over");
13212         }
13213     },
13214
13215     /**
13216      * Returns the element this component is bound to.
13217      * @return {Roo.Element}
13218      */
13219     getEl : function(){
13220         return this.el;
13221     },
13222
13223     /**
13224      * Returns the resizeChild element (or null).
13225      * @return {Roo.Element}
13226      */
13227     getResizeChild : function(){
13228         return this.resizeChild;
13229     },
13230     groupHandler : function()
13231     {
13232         
13233     },
13234     /**
13235      * Destroys this resizable. If the element was wrapped and
13236      * removeEl is not true then the element remains.
13237      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13238      */
13239     destroy : function(removeEl){
13240         this.proxy.remove();
13241         if(this.overlay){
13242             this.overlay.removeAllListeners();
13243             this.overlay.remove();
13244         }
13245         var ps = Roo.Resizable.positions;
13246         for(var k in ps){
13247             if(typeof ps[k] != "function" && this[ps[k]]){
13248                 var h = this[ps[k]];
13249                 h.el.removeAllListeners();
13250                 h.el.remove();
13251             }
13252         }
13253         if(removeEl){
13254             this.el.update("");
13255             this.el.remove();
13256         }
13257     }
13258 });
13259
13260 // private
13261 // hash to map config positions to true positions
13262 Roo.Resizable.positions = {
13263     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13264     hd: "hdrag"
13265 };
13266
13267 // private
13268 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13269     if(!this.tpl){
13270         // only initialize the template if resizable is used
13271         var tpl = Roo.DomHelper.createTemplate(
13272             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13273         );
13274         tpl.compile();
13275         Roo.Resizable.Handle.prototype.tpl = tpl;
13276     }
13277     this.position = pos;
13278     this.rz = rz;
13279     // show north drag fro topdra
13280     var handlepos = pos == 'hdrag' ? 'north' : pos;
13281     
13282     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13283     if (pos == 'hdrag') {
13284         this.el.setStyle('cursor', 'pointer');
13285     }
13286     this.el.unselectable();
13287     if(transparent){
13288         this.el.setOpacity(0);
13289     }
13290     this.el.on("mousedown", this.onMouseDown, this);
13291     if(!disableTrackOver){
13292         this.el.on("mouseover", this.onMouseOver, this);
13293         this.el.on("mouseout", this.onMouseOut, this);
13294     }
13295 };
13296
13297 // private
13298 Roo.Resizable.Handle.prototype = {
13299     afterResize : function(rz){
13300         Roo.log('after?');
13301         // do nothing
13302     },
13303     // private
13304     onMouseDown : function(e){
13305         this.rz.onMouseDown(this, e);
13306     },
13307     // private
13308     onMouseOver : function(e){
13309         this.rz.handleOver(this, e);
13310     },
13311     // private
13312     onMouseOut : function(e){
13313         this.rz.handleOut(this, e);
13314     }
13315 };/*
13316  * Based on:
13317  * Ext JS Library 1.1.1
13318  * Copyright(c) 2006-2007, Ext JS, LLC.
13319  *
13320  * Originally Released Under LGPL - original licence link has changed is not relivant.
13321  *
13322  * Fork - LGPL
13323  * <script type="text/javascript">
13324  */
13325
13326 /**
13327  * @class Roo.Editor
13328  * @extends Roo.Component
13329  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13330  * @constructor
13331  * Create a new Editor
13332  * @param {Roo.form.Field} field The Field object (or descendant)
13333  * @param {Object} config The config object
13334  */
13335 Roo.Editor = function(field, config){
13336     Roo.Editor.superclass.constructor.call(this, config);
13337     this.field = field;
13338     this.addEvents({
13339         /**
13340              * @event beforestartedit
13341              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13342              * false from the handler of this event.
13343              * @param {Editor} this
13344              * @param {Roo.Element} boundEl The underlying element bound to this editor
13345              * @param {Mixed} value The field value being set
13346              */
13347         "beforestartedit" : true,
13348         /**
13349              * @event startedit
13350              * Fires when this editor is displayed
13351              * @param {Roo.Element} boundEl The underlying element bound to this editor
13352              * @param {Mixed} value The starting field value
13353              */
13354         "startedit" : true,
13355         /**
13356              * @event beforecomplete
13357              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13358              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13359              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13360              * event will not fire since no edit actually occurred.
13361              * @param {Editor} this
13362              * @param {Mixed} value The current field value
13363              * @param {Mixed} startValue The original field value
13364              */
13365         "beforecomplete" : true,
13366         /**
13367              * @event complete
13368              * Fires after editing is complete and any changed value has been written to the underlying field.
13369              * @param {Editor} this
13370              * @param {Mixed} value The current field value
13371              * @param {Mixed} startValue The original field value
13372              */
13373         "complete" : true,
13374         /**
13375          * @event specialkey
13376          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13377          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13378          * @param {Roo.form.Field} this
13379          * @param {Roo.EventObject} e The event object
13380          */
13381         "specialkey" : true
13382     });
13383 };
13384
13385 Roo.extend(Roo.Editor, Roo.Component, {
13386     /**
13387      * @cfg {Boolean/String} autosize
13388      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13389      * or "height" to adopt the height only (defaults to false)
13390      */
13391     /**
13392      * @cfg {Boolean} revertInvalid
13393      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13394      * validation fails (defaults to true)
13395      */
13396     /**
13397      * @cfg {Boolean} ignoreNoChange
13398      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13399      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13400      * will never be ignored.
13401      */
13402     /**
13403      * @cfg {Boolean} hideEl
13404      * False to keep the bound element visible while the editor is displayed (defaults to true)
13405      */
13406     /**
13407      * @cfg {Mixed} value
13408      * The data value of the underlying field (defaults to "")
13409      */
13410     value : "",
13411     /**
13412      * @cfg {String} alignment
13413      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13414      */
13415     alignment: "c-c?",
13416     /**
13417      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13418      * for bottom-right shadow (defaults to "frame")
13419      */
13420     shadow : "frame",
13421     /**
13422      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13423      */
13424     constrain : false,
13425     /**
13426      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13427      */
13428     completeOnEnter : false,
13429     /**
13430      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13431      */
13432     cancelOnEsc : false,
13433     /**
13434      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13435      */
13436     updateEl : false,
13437
13438     // private
13439     onRender : function(ct, position){
13440         this.el = new Roo.Layer({
13441             shadow: this.shadow,
13442             cls: "x-editor",
13443             parentEl : ct,
13444             shim : this.shim,
13445             shadowOffset:4,
13446             id: this.id,
13447             constrain: this.constrain
13448         });
13449         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13450         if(this.field.msgTarget != 'title'){
13451             this.field.msgTarget = 'qtip';
13452         }
13453         this.field.render(this.el);
13454         if(Roo.isGecko){
13455             this.field.el.dom.setAttribute('autocomplete', 'off');
13456         }
13457         this.field.on("specialkey", this.onSpecialKey, this);
13458         if(this.swallowKeys){
13459             this.field.el.swallowEvent(['keydown','keypress']);
13460         }
13461         this.field.show();
13462         this.field.on("blur", this.onBlur, this);
13463         if(this.field.grow){
13464             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13465         }
13466     },
13467
13468     onSpecialKey : function(field, e)
13469     {
13470         //Roo.log('editor onSpecialKey');
13471         if(this.completeOnEnter && e.getKey() == e.ENTER){
13472             e.stopEvent();
13473             this.completeEdit();
13474             return;
13475         }
13476         // do not fire special key otherwise it might hide close the editor...
13477         if(e.getKey() == e.ENTER){    
13478             return;
13479         }
13480         if(this.cancelOnEsc && e.getKey() == e.ESC){
13481             this.cancelEdit();
13482             return;
13483         } 
13484         this.fireEvent('specialkey', field, e);
13485     
13486     },
13487
13488     /**
13489      * Starts the editing process and shows the editor.
13490      * @param {String/HTMLElement/Element} el The element to edit
13491      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13492       * to the innerHTML of el.
13493      */
13494     startEdit : function(el, value){
13495         if(this.editing){
13496             this.completeEdit();
13497         }
13498         this.boundEl = Roo.get(el);
13499         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13500         if(!this.rendered){
13501             this.render(this.parentEl || document.body);
13502         }
13503         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13504             return;
13505         }
13506         this.startValue = v;
13507         this.field.setValue(v);
13508         if(this.autoSize){
13509             var sz = this.boundEl.getSize();
13510             switch(this.autoSize){
13511                 case "width":
13512                 this.setSize(sz.width,  "");
13513                 break;
13514                 case "height":
13515                 this.setSize("",  sz.height);
13516                 break;
13517                 default:
13518                 this.setSize(sz.width,  sz.height);
13519             }
13520         }
13521         this.el.alignTo(this.boundEl, this.alignment);
13522         this.editing = true;
13523         if(Roo.QuickTips){
13524             Roo.QuickTips.disable();
13525         }
13526         this.show();
13527     },
13528
13529     /**
13530      * Sets the height and width of this editor.
13531      * @param {Number} width The new width
13532      * @param {Number} height The new height
13533      */
13534     setSize : function(w, h){
13535         this.field.setSize(w, h);
13536         if(this.el){
13537             this.el.sync();
13538         }
13539     },
13540
13541     /**
13542      * Realigns the editor to the bound field based on the current alignment config value.
13543      */
13544     realign : function(){
13545         this.el.alignTo(this.boundEl, this.alignment);
13546     },
13547
13548     /**
13549      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13550      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13551      */
13552     completeEdit : function(remainVisible){
13553         if(!this.editing){
13554             return;
13555         }
13556         var v = this.getValue();
13557         if(this.revertInvalid !== false && !this.field.isValid()){
13558             v = this.startValue;
13559             this.cancelEdit(true);
13560         }
13561         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13562             this.editing = false;
13563             this.hide();
13564             return;
13565         }
13566         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13567             this.editing = false;
13568             if(this.updateEl && this.boundEl){
13569                 this.boundEl.update(v);
13570             }
13571             if(remainVisible !== true){
13572                 this.hide();
13573             }
13574             this.fireEvent("complete", this, v, this.startValue);
13575         }
13576     },
13577
13578     // private
13579     onShow : function(){
13580         this.el.show();
13581         if(this.hideEl !== false){
13582             this.boundEl.hide();
13583         }
13584         this.field.show();
13585         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13586             this.fixIEFocus = true;
13587             this.deferredFocus.defer(50, this);
13588         }else{
13589             this.field.focus();
13590         }
13591         this.fireEvent("startedit", this.boundEl, this.startValue);
13592     },
13593
13594     deferredFocus : function(){
13595         if(this.editing){
13596             this.field.focus();
13597         }
13598     },
13599
13600     /**
13601      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13602      * reverted to the original starting value.
13603      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13604      * cancel (defaults to false)
13605      */
13606     cancelEdit : function(remainVisible){
13607         if(this.editing){
13608             this.setValue(this.startValue);
13609             if(remainVisible !== true){
13610                 this.hide();
13611             }
13612         }
13613     },
13614
13615     // private
13616     onBlur : function(){
13617         if(this.allowBlur !== true && this.editing){
13618             this.completeEdit();
13619         }
13620     },
13621
13622     // private
13623     onHide : function(){
13624         if(this.editing){
13625             this.completeEdit();
13626             return;
13627         }
13628         this.field.blur();
13629         if(this.field.collapse){
13630             this.field.collapse();
13631         }
13632         this.el.hide();
13633         if(this.hideEl !== false){
13634             this.boundEl.show();
13635         }
13636         if(Roo.QuickTips){
13637             Roo.QuickTips.enable();
13638         }
13639     },
13640
13641     /**
13642      * Sets the data value of the editor
13643      * @param {Mixed} value Any valid value supported by the underlying field
13644      */
13645     setValue : function(v){
13646         this.field.setValue(v);
13647     },
13648
13649     /**
13650      * Gets the data value of the editor
13651      * @return {Mixed} The data value
13652      */
13653     getValue : function(){
13654         return this.field.getValue();
13655     }
13656 });/*
13657  * Based on:
13658  * Ext JS Library 1.1.1
13659  * Copyright(c) 2006-2007, Ext JS, LLC.
13660  *
13661  * Originally Released Under LGPL - original licence link has changed is not relivant.
13662  *
13663  * Fork - LGPL
13664  * <script type="text/javascript">
13665  */
13666  
13667 /**
13668  * @class Roo.BasicDialog
13669  * @extends Roo.util.Observable
13670  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13671  * <pre><code>
13672 var dlg = new Roo.BasicDialog("my-dlg", {
13673     height: 200,
13674     width: 300,
13675     minHeight: 100,
13676     minWidth: 150,
13677     modal: true,
13678     proxyDrag: true,
13679     shadow: true
13680 });
13681 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13682 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13683 dlg.addButton('Cancel', dlg.hide, dlg);
13684 dlg.show();
13685 </code></pre>
13686   <b>A Dialog should always be a direct child of the body element.</b>
13687  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13688  * @cfg {String} title Default text to display in the title bar (defaults to null)
13689  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13690  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13691  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13692  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13693  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13694  * (defaults to null with no animation)
13695  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13696  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13697  * property for valid values (defaults to 'all')
13698  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13699  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13700  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13701  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13702  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13703  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13704  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13705  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13706  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13707  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13708  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13709  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13710  * draggable = true (defaults to false)
13711  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13712  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13713  * shadow (defaults to false)
13714  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13715  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13716  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13717  * @cfg {Array} buttons Array of buttons
13718  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13719  * @constructor
13720  * Create a new BasicDialog.
13721  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13722  * @param {Object} config Configuration options
13723  */
13724 Roo.BasicDialog = function(el, config){
13725     this.el = Roo.get(el);
13726     var dh = Roo.DomHelper;
13727     if(!this.el && config && config.autoCreate){
13728         if(typeof config.autoCreate == "object"){
13729             if(!config.autoCreate.id){
13730                 config.autoCreate.id = el;
13731             }
13732             this.el = dh.append(document.body,
13733                         config.autoCreate, true);
13734         }else{
13735             this.el = dh.append(document.body,
13736                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13737         }
13738     }
13739     el = this.el;
13740     el.setDisplayed(true);
13741     el.hide = this.hideAction;
13742     this.id = el.id;
13743     el.addClass("x-dlg");
13744
13745     Roo.apply(this, config);
13746
13747     this.proxy = el.createProxy("x-dlg-proxy");
13748     this.proxy.hide = this.hideAction;
13749     this.proxy.setOpacity(.5);
13750     this.proxy.hide();
13751
13752     if(config.width){
13753         el.setWidth(config.width);
13754     }
13755     if(config.height){
13756         el.setHeight(config.height);
13757     }
13758     this.size = el.getSize();
13759     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13760         this.xy = [config.x,config.y];
13761     }else{
13762         this.xy = el.getCenterXY(true);
13763     }
13764     /** The header element @type Roo.Element */
13765     this.header = el.child("> .x-dlg-hd");
13766     /** The body element @type Roo.Element */
13767     this.body = el.child("> .x-dlg-bd");
13768     /** The footer element @type Roo.Element */
13769     this.footer = el.child("> .x-dlg-ft");
13770
13771     if(!this.header){
13772         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13773     }
13774     if(!this.body){
13775         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13776     }
13777
13778     this.header.unselectable();
13779     if(this.title){
13780         this.header.update(this.title);
13781     }
13782     // this element allows the dialog to be focused for keyboard event
13783     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13784     this.focusEl.swallowEvent("click", true);
13785
13786     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13787
13788     // wrap the body and footer for special rendering
13789     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13790     if(this.footer){
13791         this.bwrap.dom.appendChild(this.footer.dom);
13792     }
13793
13794     this.bg = this.el.createChild({
13795         tag: "div", cls:"x-dlg-bg",
13796         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13797     });
13798     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13799
13800
13801     if(this.autoScroll !== false && !this.autoTabs){
13802         this.body.setStyle("overflow", "auto");
13803     }
13804
13805     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13806
13807     if(this.closable !== false){
13808         this.el.addClass("x-dlg-closable");
13809         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13810         this.close.on("click", this.closeClick, this);
13811         this.close.addClassOnOver("x-dlg-close-over");
13812     }
13813     if(this.collapsible !== false){
13814         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13815         this.collapseBtn.on("click", this.collapseClick, this);
13816         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13817         this.header.on("dblclick", this.collapseClick, this);
13818     }
13819     if(this.resizable !== false){
13820         this.el.addClass("x-dlg-resizable");
13821         this.resizer = new Roo.Resizable(el, {
13822             minWidth: this.minWidth || 80,
13823             minHeight:this.minHeight || 80,
13824             handles: this.resizeHandles || "all",
13825             pinned: true
13826         });
13827         this.resizer.on("beforeresize", this.beforeResize, this);
13828         this.resizer.on("resize", this.onResize, this);
13829     }
13830     if(this.draggable !== false){
13831         el.addClass("x-dlg-draggable");
13832         if (!this.proxyDrag) {
13833             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13834         }
13835         else {
13836             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13837         }
13838         dd.setHandleElId(this.header.id);
13839         dd.endDrag = this.endMove.createDelegate(this);
13840         dd.startDrag = this.startMove.createDelegate(this);
13841         dd.onDrag = this.onDrag.createDelegate(this);
13842         dd.scroll = false;
13843         this.dd = dd;
13844     }
13845     if(this.modal){
13846         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13847         this.mask.enableDisplayMode("block");
13848         this.mask.hide();
13849         this.el.addClass("x-dlg-modal");
13850     }
13851     if(this.shadow){
13852         this.shadow = new Roo.Shadow({
13853             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13854             offset : this.shadowOffset
13855         });
13856     }else{
13857         this.shadowOffset = 0;
13858     }
13859     if(Roo.useShims && this.shim !== false){
13860         this.shim = this.el.createShim();
13861         this.shim.hide = this.hideAction;
13862         this.shim.hide();
13863     }else{
13864         this.shim = false;
13865     }
13866     if(this.autoTabs){
13867         this.initTabs();
13868     }
13869     if (this.buttons) { 
13870         var bts= this.buttons;
13871         this.buttons = [];
13872         Roo.each(bts, function(b) {
13873             this.addButton(b);
13874         }, this);
13875     }
13876     
13877     
13878     this.addEvents({
13879         /**
13880          * @event keydown
13881          * Fires when a key is pressed
13882          * @param {Roo.BasicDialog} this
13883          * @param {Roo.EventObject} e
13884          */
13885         "keydown" : true,
13886         /**
13887          * @event move
13888          * Fires when this dialog is moved by the user.
13889          * @param {Roo.BasicDialog} this
13890          * @param {Number} x The new page X
13891          * @param {Number} y The new page Y
13892          */
13893         "move" : true,
13894         /**
13895          * @event resize
13896          * Fires when this dialog is resized by the user.
13897          * @param {Roo.BasicDialog} this
13898          * @param {Number} width The new width
13899          * @param {Number} height The new height
13900          */
13901         "resize" : true,
13902         /**
13903          * @event beforehide
13904          * Fires before this dialog is hidden.
13905          * @param {Roo.BasicDialog} this
13906          */
13907         "beforehide" : true,
13908         /**
13909          * @event hide
13910          * Fires when this dialog is hidden.
13911          * @param {Roo.BasicDialog} this
13912          */
13913         "hide" : true,
13914         /**
13915          * @event beforeshow
13916          * Fires before this dialog is shown.
13917          * @param {Roo.BasicDialog} this
13918          */
13919         "beforeshow" : true,
13920         /**
13921          * @event show
13922          * Fires when this dialog is shown.
13923          * @param {Roo.BasicDialog} this
13924          */
13925         "show" : true
13926     });
13927     el.on("keydown", this.onKeyDown, this);
13928     el.on("mousedown", this.toFront, this);
13929     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
13930     this.el.hide();
13931     Roo.DialogManager.register(this);
13932     Roo.BasicDialog.superclass.constructor.call(this);
13933 };
13934
13935 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
13936     shadowOffset: Roo.isIE ? 6 : 5,
13937     minHeight: 80,
13938     minWidth: 200,
13939     minButtonWidth: 75,
13940     defaultButton: null,
13941     buttonAlign: "right",
13942     tabTag: 'div',
13943     firstShow: true,
13944
13945     /**
13946      * Sets the dialog title text
13947      * @param {String} text The title text to display
13948      * @return {Roo.BasicDialog} this
13949      */
13950     setTitle : function(text){
13951         this.header.update(text);
13952         return this;
13953     },
13954
13955     // private
13956     closeClick : function(){
13957         this.hide();
13958     },
13959
13960     // private
13961     collapseClick : function(){
13962         this[this.collapsed ? "expand" : "collapse"]();
13963     },
13964
13965     /**
13966      * Collapses the dialog to its minimized state (only the title bar is visible).
13967      * Equivalent to the user clicking the collapse dialog button.
13968      */
13969     collapse : function(){
13970         if(!this.collapsed){
13971             this.collapsed = true;
13972             this.el.addClass("x-dlg-collapsed");
13973             this.restoreHeight = this.el.getHeight();
13974             this.resizeTo(this.el.getWidth(), this.header.getHeight());
13975         }
13976     },
13977
13978     /**
13979      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
13980      * clicking the expand dialog button.
13981      */
13982     expand : function(){
13983         if(this.collapsed){
13984             this.collapsed = false;
13985             this.el.removeClass("x-dlg-collapsed");
13986             this.resizeTo(this.el.getWidth(), this.restoreHeight);
13987         }
13988     },
13989
13990     /**
13991      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
13992      * @return {Roo.TabPanel} The tabs component
13993      */
13994     initTabs : function(){
13995         var tabs = this.getTabs();
13996         while(tabs.getTab(0)){
13997             tabs.removeTab(0);
13998         }
13999         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14000             var dom = el.dom;
14001             tabs.addTab(Roo.id(dom), dom.title);
14002             dom.title = "";
14003         });
14004         tabs.activate(0);
14005         return tabs;
14006     },
14007
14008     // private
14009     beforeResize : function(){
14010         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14011     },
14012
14013     // private
14014     onResize : function(){
14015         this.refreshSize();
14016         this.syncBodyHeight();
14017         this.adjustAssets();
14018         this.focus();
14019         this.fireEvent("resize", this, this.size.width, this.size.height);
14020     },
14021
14022     // private
14023     onKeyDown : function(e){
14024         if(this.isVisible()){
14025             this.fireEvent("keydown", this, e);
14026         }
14027     },
14028
14029     /**
14030      * Resizes the dialog.
14031      * @param {Number} width
14032      * @param {Number} height
14033      * @return {Roo.BasicDialog} this
14034      */
14035     resizeTo : function(width, height){
14036         this.el.setSize(width, height);
14037         this.size = {width: width, height: height};
14038         this.syncBodyHeight();
14039         if(this.fixedcenter){
14040             this.center();
14041         }
14042         if(this.isVisible()){
14043             this.constrainXY();
14044             this.adjustAssets();
14045         }
14046         this.fireEvent("resize", this, width, height);
14047         return this;
14048     },
14049
14050
14051     /**
14052      * Resizes the dialog to fit the specified content size.
14053      * @param {Number} width
14054      * @param {Number} height
14055      * @return {Roo.BasicDialog} this
14056      */
14057     setContentSize : function(w, h){
14058         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14059         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14060         //if(!this.el.isBorderBox()){
14061             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14062             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14063         //}
14064         if(this.tabs){
14065             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14066             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14067         }
14068         this.resizeTo(w, h);
14069         return this;
14070     },
14071
14072     /**
14073      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14074      * executed in response to a particular key being pressed while the dialog is active.
14075      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14076      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14077      * @param {Function} fn The function to call
14078      * @param {Object} scope (optional) The scope of the function
14079      * @return {Roo.BasicDialog} this
14080      */
14081     addKeyListener : function(key, fn, scope){
14082         var keyCode, shift, ctrl, alt;
14083         if(typeof key == "object" && !(key instanceof Array)){
14084             keyCode = key["key"];
14085             shift = key["shift"];
14086             ctrl = key["ctrl"];
14087             alt = key["alt"];
14088         }else{
14089             keyCode = key;
14090         }
14091         var handler = function(dlg, e){
14092             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14093                 var k = e.getKey();
14094                 if(keyCode instanceof Array){
14095                     for(var i = 0, len = keyCode.length; i < len; i++){
14096                         if(keyCode[i] == k){
14097                           fn.call(scope || window, dlg, k, e);
14098                           return;
14099                         }
14100                     }
14101                 }else{
14102                     if(k == keyCode){
14103                         fn.call(scope || window, dlg, k, e);
14104                     }
14105                 }
14106             }
14107         };
14108         this.on("keydown", handler);
14109         return this;
14110     },
14111
14112     /**
14113      * Returns the TabPanel component (creates it if it doesn't exist).
14114      * Note: If you wish to simply check for the existence of tabs without creating them,
14115      * check for a null 'tabs' property.
14116      * @return {Roo.TabPanel} The tabs component
14117      */
14118     getTabs : function(){
14119         if(!this.tabs){
14120             this.el.addClass("x-dlg-auto-tabs");
14121             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14122             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14123         }
14124         return this.tabs;
14125     },
14126
14127     /**
14128      * Adds a button to the footer section of the dialog.
14129      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14130      * object or a valid Roo.DomHelper element config
14131      * @param {Function} handler The function called when the button is clicked
14132      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14133      * @return {Roo.Button} The new button
14134      */
14135     addButton : function(config, handler, scope){
14136         var dh = Roo.DomHelper;
14137         if(!this.footer){
14138             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14139         }
14140         if(!this.btnContainer){
14141             var tb = this.footer.createChild({
14142
14143                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14144                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14145             }, null, true);
14146             this.btnContainer = tb.firstChild.firstChild.firstChild;
14147         }
14148         var bconfig = {
14149             handler: handler,
14150             scope: scope,
14151             minWidth: this.minButtonWidth,
14152             hideParent:true
14153         };
14154         if(typeof config == "string"){
14155             bconfig.text = config;
14156         }else{
14157             if(config.tag){
14158                 bconfig.dhconfig = config;
14159             }else{
14160                 Roo.apply(bconfig, config);
14161             }
14162         }
14163         var fc = false;
14164         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14165             bconfig.position = Math.max(0, bconfig.position);
14166             fc = this.btnContainer.childNodes[bconfig.position];
14167         }
14168          
14169         var btn = new Roo.Button(
14170             fc ? 
14171                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14172                 : this.btnContainer.appendChild(document.createElement("td")),
14173             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14174             bconfig
14175         );
14176         this.syncBodyHeight();
14177         if(!this.buttons){
14178             /**
14179              * Array of all the buttons that have been added to this dialog via addButton
14180              * @type Array
14181              */
14182             this.buttons = [];
14183         }
14184         this.buttons.push(btn);
14185         return btn;
14186     },
14187
14188     /**
14189      * Sets the default button to be focused when the dialog is displayed.
14190      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14191      * @return {Roo.BasicDialog} this
14192      */
14193     setDefaultButton : function(btn){
14194         this.defaultButton = btn;
14195         return this;
14196     },
14197
14198     // private
14199     getHeaderFooterHeight : function(safe){
14200         var height = 0;
14201         if(this.header){
14202            height += this.header.getHeight();
14203         }
14204         if(this.footer){
14205            var fm = this.footer.getMargins();
14206             height += (this.footer.getHeight()+fm.top+fm.bottom);
14207         }
14208         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14209         height += this.centerBg.getPadding("tb");
14210         return height;
14211     },
14212
14213     // private
14214     syncBodyHeight : function()
14215     {
14216         var bd = this.body, // the text
14217             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14218             bw = this.bwrap;
14219         var height = this.size.height - this.getHeaderFooterHeight(false);
14220         bd.setHeight(height-bd.getMargins("tb"));
14221         var hh = this.header.getHeight();
14222         var h = this.size.height-hh;
14223         cb.setHeight(h);
14224         
14225         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14226         bw.setHeight(h-cb.getPadding("tb"));
14227         
14228         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14229         bd.setWidth(bw.getWidth(true));
14230         if(this.tabs){
14231             this.tabs.syncHeight();
14232             if(Roo.isIE){
14233                 this.tabs.el.repaint();
14234             }
14235         }
14236     },
14237
14238     /**
14239      * Restores the previous state of the dialog if Roo.state is configured.
14240      * @return {Roo.BasicDialog} this
14241      */
14242     restoreState : function(){
14243         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14244         if(box && box.width){
14245             this.xy = [box.x, box.y];
14246             this.resizeTo(box.width, box.height);
14247         }
14248         return this;
14249     },
14250
14251     // private
14252     beforeShow : function(){
14253         this.expand();
14254         if(this.fixedcenter){
14255             this.xy = this.el.getCenterXY(true);
14256         }
14257         if(this.modal){
14258             Roo.get(document.body).addClass("x-body-masked");
14259             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14260             this.mask.show();
14261         }
14262         this.constrainXY();
14263     },
14264
14265     // private
14266     animShow : function(){
14267         var b = Roo.get(this.animateTarget).getBox();
14268         this.proxy.setSize(b.width, b.height);
14269         this.proxy.setLocation(b.x, b.y);
14270         this.proxy.show();
14271         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14272                     true, .35, this.showEl.createDelegate(this));
14273     },
14274
14275     /**
14276      * Shows the dialog.
14277      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14278      * @return {Roo.BasicDialog} this
14279      */
14280     show : function(animateTarget){
14281         if (this.fireEvent("beforeshow", this) === false){
14282             return;
14283         }
14284         if(this.syncHeightBeforeShow){
14285             this.syncBodyHeight();
14286         }else if(this.firstShow){
14287             this.firstShow = false;
14288             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14289         }
14290         this.animateTarget = animateTarget || this.animateTarget;
14291         if(!this.el.isVisible()){
14292             this.beforeShow();
14293             if(this.animateTarget && Roo.get(this.animateTarget)){
14294                 this.animShow();
14295             }else{
14296                 this.showEl();
14297             }
14298         }
14299         return this;
14300     },
14301
14302     // private
14303     showEl : function(){
14304         this.proxy.hide();
14305         this.el.setXY(this.xy);
14306         this.el.show();
14307         this.adjustAssets(true);
14308         this.toFront();
14309         this.focus();
14310         // IE peekaboo bug - fix found by Dave Fenwick
14311         if(Roo.isIE){
14312             this.el.repaint();
14313         }
14314         this.fireEvent("show", this);
14315     },
14316
14317     /**
14318      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14319      * dialog itself will receive focus.
14320      */
14321     focus : function(){
14322         if(this.defaultButton){
14323             this.defaultButton.focus();
14324         }else{
14325             this.focusEl.focus();
14326         }
14327     },
14328
14329     // private
14330     constrainXY : function(){
14331         if(this.constraintoviewport !== false){
14332             if(!this.viewSize){
14333                 if(this.container){
14334                     var s = this.container.getSize();
14335                     this.viewSize = [s.width, s.height];
14336                 }else{
14337                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14338                 }
14339             }
14340             var s = Roo.get(this.container||document).getScroll();
14341
14342             var x = this.xy[0], y = this.xy[1];
14343             var w = this.size.width, h = this.size.height;
14344             var vw = this.viewSize[0], vh = this.viewSize[1];
14345             // only move it if it needs it
14346             var moved = false;
14347             // first validate right/bottom
14348             if(x + w > vw+s.left){
14349                 x = vw - w;
14350                 moved = true;
14351             }
14352             if(y + h > vh+s.top){
14353                 y = vh - h;
14354                 moved = true;
14355             }
14356             // then make sure top/left isn't negative
14357             if(x < s.left){
14358                 x = s.left;
14359                 moved = true;
14360             }
14361             if(y < s.top){
14362                 y = s.top;
14363                 moved = true;
14364             }
14365             if(moved){
14366                 // cache xy
14367                 this.xy = [x, y];
14368                 if(this.isVisible()){
14369                     this.el.setLocation(x, y);
14370                     this.adjustAssets();
14371                 }
14372             }
14373         }
14374     },
14375
14376     // private
14377     onDrag : function(){
14378         if(!this.proxyDrag){
14379             this.xy = this.el.getXY();
14380             this.adjustAssets();
14381         }
14382     },
14383
14384     // private
14385     adjustAssets : function(doShow){
14386         var x = this.xy[0], y = this.xy[1];
14387         var w = this.size.width, h = this.size.height;
14388         if(doShow === true){
14389             if(this.shadow){
14390                 this.shadow.show(this.el);
14391             }
14392             if(this.shim){
14393                 this.shim.show();
14394             }
14395         }
14396         if(this.shadow && this.shadow.isVisible()){
14397             this.shadow.show(this.el);
14398         }
14399         if(this.shim && this.shim.isVisible()){
14400             this.shim.setBounds(x, y, w, h);
14401         }
14402     },
14403
14404     // private
14405     adjustViewport : function(w, h){
14406         if(!w || !h){
14407             w = Roo.lib.Dom.getViewWidth();
14408             h = Roo.lib.Dom.getViewHeight();
14409         }
14410         // cache the size
14411         this.viewSize = [w, h];
14412         if(this.modal && this.mask.isVisible()){
14413             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14414             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14415         }
14416         if(this.isVisible()){
14417             this.constrainXY();
14418         }
14419     },
14420
14421     /**
14422      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14423      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14424      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14425      */
14426     destroy : function(removeEl){
14427         if(this.isVisible()){
14428             this.animateTarget = null;
14429             this.hide();
14430         }
14431         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14432         if(this.tabs){
14433             this.tabs.destroy(removeEl);
14434         }
14435         Roo.destroy(
14436              this.shim,
14437              this.proxy,
14438              this.resizer,
14439              this.close,
14440              this.mask
14441         );
14442         if(this.dd){
14443             this.dd.unreg();
14444         }
14445         if(this.buttons){
14446            for(var i = 0, len = this.buttons.length; i < len; i++){
14447                this.buttons[i].destroy();
14448            }
14449         }
14450         this.el.removeAllListeners();
14451         if(removeEl === true){
14452             this.el.update("");
14453             this.el.remove();
14454         }
14455         Roo.DialogManager.unregister(this);
14456     },
14457
14458     // private
14459     startMove : function(){
14460         if(this.proxyDrag){
14461             this.proxy.show();
14462         }
14463         if(this.constraintoviewport !== false){
14464             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14465         }
14466     },
14467
14468     // private
14469     endMove : function(){
14470         if(!this.proxyDrag){
14471             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14472         }else{
14473             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14474             this.proxy.hide();
14475         }
14476         this.refreshSize();
14477         this.adjustAssets();
14478         this.focus();
14479         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14480     },
14481
14482     /**
14483      * Brings this dialog to the front of any other visible dialogs
14484      * @return {Roo.BasicDialog} this
14485      */
14486     toFront : function(){
14487         Roo.DialogManager.bringToFront(this);
14488         return this;
14489     },
14490
14491     /**
14492      * Sends this dialog to the back (under) of any other visible dialogs
14493      * @return {Roo.BasicDialog} this
14494      */
14495     toBack : function(){
14496         Roo.DialogManager.sendToBack(this);
14497         return this;
14498     },
14499
14500     /**
14501      * Centers this dialog in the viewport
14502      * @return {Roo.BasicDialog} this
14503      */
14504     center : function(){
14505         var xy = this.el.getCenterXY(true);
14506         this.moveTo(xy[0], xy[1]);
14507         return this;
14508     },
14509
14510     /**
14511      * Moves the dialog's top-left corner to the specified point
14512      * @param {Number} x
14513      * @param {Number} y
14514      * @return {Roo.BasicDialog} this
14515      */
14516     moveTo : function(x, y){
14517         this.xy = [x,y];
14518         if(this.isVisible()){
14519             this.el.setXY(this.xy);
14520             this.adjustAssets();
14521         }
14522         return this;
14523     },
14524
14525     /**
14526      * Aligns the dialog to the specified element
14527      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14528      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14529      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14530      * @return {Roo.BasicDialog} this
14531      */
14532     alignTo : function(element, position, offsets){
14533         this.xy = this.el.getAlignToXY(element, position, offsets);
14534         if(this.isVisible()){
14535             this.el.setXY(this.xy);
14536             this.adjustAssets();
14537         }
14538         return this;
14539     },
14540
14541     /**
14542      * Anchors an element to another element and realigns it when the window is resized.
14543      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14544      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14545      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14546      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14547      * is a number, it is used as the buffer delay (defaults to 50ms).
14548      * @return {Roo.BasicDialog} this
14549      */
14550     anchorTo : function(el, alignment, offsets, monitorScroll){
14551         var action = function(){
14552             this.alignTo(el, alignment, offsets);
14553         };
14554         Roo.EventManager.onWindowResize(action, this);
14555         var tm = typeof monitorScroll;
14556         if(tm != 'undefined'){
14557             Roo.EventManager.on(window, 'scroll', action, this,
14558                 {buffer: tm == 'number' ? monitorScroll : 50});
14559         }
14560         action.call(this);
14561         return this;
14562     },
14563
14564     /**
14565      * Returns true if the dialog is visible
14566      * @return {Boolean}
14567      */
14568     isVisible : function(){
14569         return this.el.isVisible();
14570     },
14571
14572     // private
14573     animHide : function(callback){
14574         var b = Roo.get(this.animateTarget).getBox();
14575         this.proxy.show();
14576         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14577         this.el.hide();
14578         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14579                     this.hideEl.createDelegate(this, [callback]));
14580     },
14581
14582     /**
14583      * Hides the dialog.
14584      * @param {Function} callback (optional) Function to call when the dialog is hidden
14585      * @return {Roo.BasicDialog} this
14586      */
14587     hide : function(callback){
14588         if (this.fireEvent("beforehide", this) === false){
14589             return;
14590         }
14591         if(this.shadow){
14592             this.shadow.hide();
14593         }
14594         if(this.shim) {
14595           this.shim.hide();
14596         }
14597         // sometimes animateTarget seems to get set.. causing problems...
14598         // this just double checks..
14599         if(this.animateTarget && Roo.get(this.animateTarget)) {
14600            this.animHide(callback);
14601         }else{
14602             this.el.hide();
14603             this.hideEl(callback);
14604         }
14605         return this;
14606     },
14607
14608     // private
14609     hideEl : function(callback){
14610         this.proxy.hide();
14611         if(this.modal){
14612             this.mask.hide();
14613             Roo.get(document.body).removeClass("x-body-masked");
14614         }
14615         this.fireEvent("hide", this);
14616         if(typeof callback == "function"){
14617             callback();
14618         }
14619     },
14620
14621     // private
14622     hideAction : function(){
14623         this.setLeft("-10000px");
14624         this.setTop("-10000px");
14625         this.setStyle("visibility", "hidden");
14626     },
14627
14628     // private
14629     refreshSize : function(){
14630         this.size = this.el.getSize();
14631         this.xy = this.el.getXY();
14632         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14633     },
14634
14635     // private
14636     // z-index is managed by the DialogManager and may be overwritten at any time
14637     setZIndex : function(index){
14638         if(this.modal){
14639             this.mask.setStyle("z-index", index);
14640         }
14641         if(this.shim){
14642             this.shim.setStyle("z-index", ++index);
14643         }
14644         if(this.shadow){
14645             this.shadow.setZIndex(++index);
14646         }
14647         this.el.setStyle("z-index", ++index);
14648         if(this.proxy){
14649             this.proxy.setStyle("z-index", ++index);
14650         }
14651         if(this.resizer){
14652             this.resizer.proxy.setStyle("z-index", ++index);
14653         }
14654
14655         this.lastZIndex = index;
14656     },
14657
14658     /**
14659      * Returns the element for this dialog
14660      * @return {Roo.Element} The underlying dialog Element
14661      */
14662     getEl : function(){
14663         return this.el;
14664     }
14665 });
14666
14667 /**
14668  * @class Roo.DialogManager
14669  * Provides global access to BasicDialogs that have been created and
14670  * support for z-indexing (layering) multiple open dialogs.
14671  */
14672 Roo.DialogManager = function(){
14673     var list = {};
14674     var accessList = [];
14675     var front = null;
14676
14677     // private
14678     var sortDialogs = function(d1, d2){
14679         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14680     };
14681
14682     // private
14683     var orderDialogs = function(){
14684         accessList.sort(sortDialogs);
14685         var seed = Roo.DialogManager.zseed;
14686         for(var i = 0, len = accessList.length; i < len; i++){
14687             var dlg = accessList[i];
14688             if(dlg){
14689                 dlg.setZIndex(seed + (i*10));
14690             }
14691         }
14692     };
14693
14694     return {
14695         /**
14696          * The starting z-index for BasicDialogs (defaults to 9000)
14697          * @type Number The z-index value
14698          */
14699         zseed : 9000,
14700
14701         // private
14702         register : function(dlg){
14703             list[dlg.id] = dlg;
14704             accessList.push(dlg);
14705         },
14706
14707         // private
14708         unregister : function(dlg){
14709             delete list[dlg.id];
14710             var i=0;
14711             var len=0;
14712             if(!accessList.indexOf){
14713                 for(  i = 0, len = accessList.length; i < len; i++){
14714                     if(accessList[i] == dlg){
14715                         accessList.splice(i, 1);
14716                         return;
14717                     }
14718                 }
14719             }else{
14720                  i = accessList.indexOf(dlg);
14721                 if(i != -1){
14722                     accessList.splice(i, 1);
14723                 }
14724             }
14725         },
14726
14727         /**
14728          * Gets a registered dialog by id
14729          * @param {String/Object} id The id of the dialog or a dialog
14730          * @return {Roo.BasicDialog} this
14731          */
14732         get : function(id){
14733             return typeof id == "object" ? id : list[id];
14734         },
14735
14736         /**
14737          * Brings the specified dialog to the front
14738          * @param {String/Object} dlg The id of the dialog or a dialog
14739          * @return {Roo.BasicDialog} this
14740          */
14741         bringToFront : function(dlg){
14742             dlg = this.get(dlg);
14743             if(dlg != front){
14744                 front = dlg;
14745                 dlg._lastAccess = new Date().getTime();
14746                 orderDialogs();
14747             }
14748             return dlg;
14749         },
14750
14751         /**
14752          * Sends the specified dialog to the back
14753          * @param {String/Object} dlg The id of the dialog or a dialog
14754          * @return {Roo.BasicDialog} this
14755          */
14756         sendToBack : function(dlg){
14757             dlg = this.get(dlg);
14758             dlg._lastAccess = -(new Date().getTime());
14759             orderDialogs();
14760             return dlg;
14761         },
14762
14763         /**
14764          * Hides all dialogs
14765          */
14766         hideAll : function(){
14767             for(var id in list){
14768                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14769                     list[id].hide();
14770                 }
14771             }
14772         }
14773     };
14774 }();
14775
14776 /**
14777  * @class Roo.LayoutDialog
14778  * @extends Roo.BasicDialog
14779  * Dialog which provides adjustments for working with a layout in a Dialog.
14780  * Add your necessary layout config options to the dialog's config.<br>
14781  * Example usage (including a nested layout):
14782  * <pre><code>
14783 if(!dialog){
14784     dialog = new Roo.LayoutDialog("download-dlg", {
14785         modal: true,
14786         width:600,
14787         height:450,
14788         shadow:true,
14789         minWidth:500,
14790         minHeight:350,
14791         autoTabs:true,
14792         proxyDrag:true,
14793         // layout config merges with the dialog config
14794         center:{
14795             tabPosition: "top",
14796             alwaysShowTabs: true
14797         }
14798     });
14799     dialog.addKeyListener(27, dialog.hide, dialog);
14800     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14801     dialog.addButton("Build It!", this.getDownload, this);
14802
14803     // we can even add nested layouts
14804     var innerLayout = new Roo.BorderLayout("dl-inner", {
14805         east: {
14806             initialSize: 200,
14807             autoScroll:true,
14808             split:true
14809         },
14810         center: {
14811             autoScroll:true
14812         }
14813     });
14814     innerLayout.beginUpdate();
14815     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14816     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14817     innerLayout.endUpdate(true);
14818
14819     var layout = dialog.getLayout();
14820     layout.beginUpdate();
14821     layout.add("center", new Roo.ContentPanel("standard-panel",
14822                         {title: "Download the Source", fitToFrame:true}));
14823     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14824                {title: "Build your own roo.js"}));
14825     layout.getRegion("center").showPanel(sp);
14826     layout.endUpdate();
14827 }
14828 </code></pre>
14829     * @constructor
14830     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14831     * @param {Object} config configuration options
14832   */
14833 Roo.LayoutDialog = function(el, cfg){
14834     
14835     var config=  cfg;
14836     if (typeof(cfg) == 'undefined') {
14837         config = Roo.apply({}, el);
14838         // not sure why we use documentElement here.. - it should always be body.
14839         // IE7 borks horribly if we use documentElement.
14840         // webkit also does not like documentElement - it creates a body element...
14841         el = Roo.get( document.body || document.documentElement ).createChild();
14842         //config.autoCreate = true;
14843     }
14844     
14845     
14846     config.autoTabs = false;
14847     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14848     this.body.setStyle({overflow:"hidden", position:"relative"});
14849     this.layout = new Roo.BorderLayout(this.body.dom, config);
14850     this.layout.monitorWindowResize = false;
14851     this.el.addClass("x-dlg-auto-layout");
14852     // fix case when center region overwrites center function
14853     this.center = Roo.BasicDialog.prototype.center;
14854     this.on("show", this.layout.layout, this.layout, true);
14855     if (config.items) {
14856         var xitems = config.items;
14857         delete config.items;
14858         Roo.each(xitems, this.addxtype, this);
14859     }
14860     
14861     
14862 };
14863 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14864     /**
14865      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14866      * @deprecated
14867      */
14868     endUpdate : function(){
14869         this.layout.endUpdate();
14870     },
14871
14872     /**
14873      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14874      *  @deprecated
14875      */
14876     beginUpdate : function(){
14877         this.layout.beginUpdate();
14878     },
14879
14880     /**
14881      * Get the BorderLayout for this dialog
14882      * @return {Roo.BorderLayout}
14883      */
14884     getLayout : function(){
14885         return this.layout;
14886     },
14887
14888     showEl : function(){
14889         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14890         if(Roo.isIE7){
14891             this.layout.layout();
14892         }
14893     },
14894
14895     // private
14896     // Use the syncHeightBeforeShow config option to control this automatically
14897     syncBodyHeight : function(){
14898         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14899         if(this.layout){this.layout.layout();}
14900     },
14901     
14902       /**
14903      * Add an xtype element (actually adds to the layout.)
14904      * @return {Object} xdata xtype object data.
14905      */
14906     
14907     addxtype : function(c) {
14908         return this.layout.addxtype(c);
14909     }
14910 });/*
14911  * Based on:
14912  * Ext JS Library 1.1.1
14913  * Copyright(c) 2006-2007, Ext JS, LLC.
14914  *
14915  * Originally Released Under LGPL - original licence link has changed is not relivant.
14916  *
14917  * Fork - LGPL
14918  * <script type="text/javascript">
14919  */
14920  
14921 /**
14922  * @class Roo.MessageBox
14923  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
14924  * Example usage:
14925  *<pre><code>
14926 // Basic alert:
14927 Roo.Msg.alert('Status', 'Changes saved successfully.');
14928
14929 // Prompt for user data:
14930 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
14931     if (btn == 'ok'){
14932         // process text value...
14933     }
14934 });
14935
14936 // Show a dialog using config options:
14937 Roo.Msg.show({
14938    title:'Save Changes?',
14939    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
14940    buttons: Roo.Msg.YESNOCANCEL,
14941    fn: processResult,
14942    animEl: 'elId'
14943 });
14944 </code></pre>
14945  * @singleton
14946  */
14947 Roo.MessageBox = function(){
14948     var dlg, opt, mask, waitTimer;
14949     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
14950     var buttons, activeTextEl, bwidth;
14951
14952     // private
14953     var handleButton = function(button){
14954         dlg.hide();
14955         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
14956     };
14957
14958     // private
14959     var handleHide = function(){
14960         if(opt && opt.cls){
14961             dlg.el.removeClass(opt.cls);
14962         }
14963         if(waitTimer){
14964             Roo.TaskMgr.stop(waitTimer);
14965             waitTimer = null;
14966         }
14967     };
14968
14969     // private
14970     var updateButtons = function(b){
14971         var width = 0;
14972         if(!b){
14973             buttons["ok"].hide();
14974             buttons["cancel"].hide();
14975             buttons["yes"].hide();
14976             buttons["no"].hide();
14977             dlg.footer.dom.style.display = 'none';
14978             return width;
14979         }
14980         dlg.footer.dom.style.display = '';
14981         for(var k in buttons){
14982             if(typeof buttons[k] != "function"){
14983                 if(b[k]){
14984                     buttons[k].show();
14985                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
14986                     width += buttons[k].el.getWidth()+15;
14987                 }else{
14988                     buttons[k].hide();
14989                 }
14990             }
14991         }
14992         return width;
14993     };
14994
14995     // private
14996     var handleEsc = function(d, k, e){
14997         if(opt && opt.closable !== false){
14998             dlg.hide();
14999         }
15000         if(e){
15001             e.stopEvent();
15002         }
15003     };
15004
15005     return {
15006         /**
15007          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15008          * @return {Roo.BasicDialog} The BasicDialog element
15009          */
15010         getDialog : function(){
15011            if(!dlg){
15012                 dlg = new Roo.BasicDialog("x-msg-box", {
15013                     autoCreate : true,
15014                     shadow: true,
15015                     draggable: true,
15016                     resizable:false,
15017                     constraintoviewport:false,
15018                     fixedcenter:true,
15019                     collapsible : false,
15020                     shim:true,
15021                     modal: true,
15022                     width:400, height:100,
15023                     buttonAlign:"center",
15024                     closeClick : function(){
15025                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15026                             handleButton("no");
15027                         }else{
15028                             handleButton("cancel");
15029                         }
15030                     }
15031                 });
15032                 dlg.on("hide", handleHide);
15033                 mask = dlg.mask;
15034                 dlg.addKeyListener(27, handleEsc);
15035                 buttons = {};
15036                 var bt = this.buttonText;
15037                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15038                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15039                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15040                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15041                 bodyEl = dlg.body.createChild({
15042
15043                     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>'
15044                 });
15045                 msgEl = bodyEl.dom.firstChild;
15046                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15047                 textboxEl.enableDisplayMode();
15048                 textboxEl.addKeyListener([10,13], function(){
15049                     if(dlg.isVisible() && opt && opt.buttons){
15050                         if(opt.buttons.ok){
15051                             handleButton("ok");
15052                         }else if(opt.buttons.yes){
15053                             handleButton("yes");
15054                         }
15055                     }
15056                 });
15057                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15058                 textareaEl.enableDisplayMode();
15059                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15060                 progressEl.enableDisplayMode();
15061                 var pf = progressEl.dom.firstChild;
15062                 if (pf) {
15063                     pp = Roo.get(pf.firstChild);
15064                     pp.setHeight(pf.offsetHeight);
15065                 }
15066                 
15067             }
15068             return dlg;
15069         },
15070
15071         /**
15072          * Updates the message box body text
15073          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15074          * the XHTML-compliant non-breaking space character '&amp;#160;')
15075          * @return {Roo.MessageBox} This message box
15076          */
15077         updateText : function(text){
15078             if(!dlg.isVisible() && !opt.width){
15079                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15080             }
15081             msgEl.innerHTML = text || '&#160;';
15082       
15083             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15084             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15085             var w = Math.max(
15086                     Math.min(opt.width || cw , this.maxWidth), 
15087                     Math.max(opt.minWidth || this.minWidth, bwidth)
15088             );
15089             if(opt.prompt){
15090                 activeTextEl.setWidth(w);
15091             }
15092             if(dlg.isVisible()){
15093                 dlg.fixedcenter = false;
15094             }
15095             // to big, make it scroll. = But as usual stupid IE does not support
15096             // !important..
15097             
15098             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15099                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15100                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15101             } else {
15102                 bodyEl.dom.style.height = '';
15103                 bodyEl.dom.style.overflowY = '';
15104             }
15105             if (cw > w) {
15106                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15107             } else {
15108                 bodyEl.dom.style.overflowX = '';
15109             }
15110             
15111             dlg.setContentSize(w, bodyEl.getHeight());
15112             if(dlg.isVisible()){
15113                 dlg.fixedcenter = true;
15114             }
15115             return this;
15116         },
15117
15118         /**
15119          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15120          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15121          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15122          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15123          * @return {Roo.MessageBox} This message box
15124          */
15125         updateProgress : function(value, text){
15126             if(text){
15127                 this.updateText(text);
15128             }
15129             if (pp) { // weird bug on my firefox - for some reason this is not defined
15130                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15131             }
15132             return this;
15133         },        
15134
15135         /**
15136          * Returns true if the message box is currently displayed
15137          * @return {Boolean} True if the message box is visible, else false
15138          */
15139         isVisible : function(){
15140             return dlg && dlg.isVisible();  
15141         },
15142
15143         /**
15144          * Hides the message box if it is displayed
15145          */
15146         hide : function(){
15147             if(this.isVisible()){
15148                 dlg.hide();
15149             }  
15150         },
15151
15152         /**
15153          * Displays a new message box, or reinitializes an existing message box, based on the config options
15154          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15155          * The following config object properties are supported:
15156          * <pre>
15157 Property    Type             Description
15158 ----------  ---------------  ------------------------------------------------------------------------------------
15159 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15160                                    closes (defaults to undefined)
15161 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15162                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15163 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15164                                    progress and wait dialogs will ignore this property and always hide the
15165                                    close button as they can only be closed programmatically.
15166 cls               String           A custom CSS class to apply to the message box element
15167 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15168                                    displayed (defaults to 75)
15169 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15170                                    function will be btn (the name of the button that was clicked, if applicable,
15171                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15172                                    Progress and wait dialogs will ignore this option since they do not respond to
15173                                    user actions and can only be closed programmatically, so any required function
15174                                    should be called by the same code after it closes the dialog.
15175 icon              String           A CSS class that provides a background image to be used as an icon for
15176                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15177 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15178 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15179 modal             Boolean          False to allow user interaction with the page while the message box is
15180                                    displayed (defaults to true)
15181 msg               String           A string that will replace the existing message box body text (defaults
15182                                    to the XHTML-compliant non-breaking space character '&#160;')
15183 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15184 progress          Boolean          True to display a progress bar (defaults to false)
15185 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15186 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15187 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15188 title             String           The title text
15189 value             String           The string value to set into the active textbox element if displayed
15190 wait              Boolean          True to display a progress bar (defaults to false)
15191 width             Number           The width of the dialog in pixels
15192 </pre>
15193          *
15194          * Example usage:
15195          * <pre><code>
15196 Roo.Msg.show({
15197    title: 'Address',
15198    msg: 'Please enter your address:',
15199    width: 300,
15200    buttons: Roo.MessageBox.OKCANCEL,
15201    multiline: true,
15202    fn: saveAddress,
15203    animEl: 'addAddressBtn'
15204 });
15205 </code></pre>
15206          * @param {Object} config Configuration options
15207          * @return {Roo.MessageBox} This message box
15208          */
15209         show : function(options)
15210         {
15211             
15212             // this causes nightmares if you show one dialog after another
15213             // especially on callbacks..
15214              
15215             if(this.isVisible()){
15216                 
15217                 this.hide();
15218                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15219                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15220                 Roo.log("New Dialog Message:" +  options.msg )
15221                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15222                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15223                 
15224             }
15225             var d = this.getDialog();
15226             opt = options;
15227             d.setTitle(opt.title || "&#160;");
15228             d.close.setDisplayed(opt.closable !== false);
15229             activeTextEl = textboxEl;
15230             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15231             if(opt.prompt){
15232                 if(opt.multiline){
15233                     textboxEl.hide();
15234                     textareaEl.show();
15235                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15236                         opt.multiline : this.defaultTextHeight);
15237                     activeTextEl = textareaEl;
15238                 }else{
15239                     textboxEl.show();
15240                     textareaEl.hide();
15241                 }
15242             }else{
15243                 textboxEl.hide();
15244                 textareaEl.hide();
15245             }
15246             progressEl.setDisplayed(opt.progress === true);
15247             this.updateProgress(0);
15248             activeTextEl.dom.value = opt.value || "";
15249             if(opt.prompt){
15250                 dlg.setDefaultButton(activeTextEl);
15251             }else{
15252                 var bs = opt.buttons;
15253                 var db = null;
15254                 if(bs && bs.ok){
15255                     db = buttons["ok"];
15256                 }else if(bs && bs.yes){
15257                     db = buttons["yes"];
15258                 }
15259                 dlg.setDefaultButton(db);
15260             }
15261             bwidth = updateButtons(opt.buttons);
15262             this.updateText(opt.msg);
15263             if(opt.cls){
15264                 d.el.addClass(opt.cls);
15265             }
15266             d.proxyDrag = opt.proxyDrag === true;
15267             d.modal = opt.modal !== false;
15268             d.mask = opt.modal !== false ? mask : false;
15269             if(!d.isVisible()){
15270                 // force it to the end of the z-index stack so it gets a cursor in FF
15271                 document.body.appendChild(dlg.el.dom);
15272                 d.animateTarget = null;
15273                 d.show(options.animEl);
15274             }
15275             return this;
15276         },
15277
15278         /**
15279          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15280          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15281          * and closing the message box when the process is complete.
15282          * @param {String} title The title bar text
15283          * @param {String} msg The message box body text
15284          * @return {Roo.MessageBox} This message box
15285          */
15286         progress : function(title, msg){
15287             this.show({
15288                 title : title,
15289                 msg : msg,
15290                 buttons: false,
15291                 progress:true,
15292                 closable:false,
15293                 minWidth: this.minProgressWidth,
15294                 modal : true
15295             });
15296             return this;
15297         },
15298
15299         /**
15300          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15301          * If a callback function is passed it will be called after the user clicks the button, and the
15302          * id of the button that was clicked will be passed as the only parameter to the callback
15303          * (could also be the top-right close button).
15304          * @param {String} title The title bar text
15305          * @param {String} msg The message box body text
15306          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15307          * @param {Object} scope (optional) The scope of the callback function
15308          * @return {Roo.MessageBox} This message box
15309          */
15310         alert : function(title, msg, fn, scope){
15311             this.show({
15312                 title : title,
15313                 msg : msg,
15314                 buttons: this.OK,
15315                 fn: fn,
15316                 scope : scope,
15317                 modal : true
15318             });
15319             return this;
15320         },
15321
15322         /**
15323          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15324          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15325          * You are responsible for closing the message box when the process is complete.
15326          * @param {String} msg The message box body text
15327          * @param {String} title (optional) The title bar text
15328          * @return {Roo.MessageBox} This message box
15329          */
15330         wait : function(msg, title){
15331             this.show({
15332                 title : title,
15333                 msg : msg,
15334                 buttons: false,
15335                 closable:false,
15336                 progress:true,
15337                 modal:true,
15338                 width:300,
15339                 wait:true
15340             });
15341             waitTimer = Roo.TaskMgr.start({
15342                 run: function(i){
15343                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15344                 },
15345                 interval: 1000
15346             });
15347             return this;
15348         },
15349
15350         /**
15351          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15352          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15353          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15354          * @param {String} title The title bar text
15355          * @param {String} msg The message box body text
15356          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15357          * @param {Object} scope (optional) The scope of the callback function
15358          * @return {Roo.MessageBox} This message box
15359          */
15360         confirm : function(title, msg, fn, scope){
15361             this.show({
15362                 title : title,
15363                 msg : msg,
15364                 buttons: this.YESNO,
15365                 fn: fn,
15366                 scope : scope,
15367                 modal : true
15368             });
15369             return this;
15370         },
15371
15372         /**
15373          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15374          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15375          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15376          * (could also be the top-right close button) and the text that was entered will be passed as the two
15377          * parameters to the callback.
15378          * @param {String} title The title bar text
15379          * @param {String} msg The message box body text
15380          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15381          * @param {Object} scope (optional) The scope of the callback function
15382          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15383          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15384          * @return {Roo.MessageBox} This message box
15385          */
15386         prompt : function(title, msg, fn, scope, multiline){
15387             this.show({
15388                 title : title,
15389                 msg : msg,
15390                 buttons: this.OKCANCEL,
15391                 fn: fn,
15392                 minWidth:250,
15393                 scope : scope,
15394                 prompt:true,
15395                 multiline: multiline,
15396                 modal : true
15397             });
15398             return this;
15399         },
15400
15401         /**
15402          * Button config that displays a single OK button
15403          * @type Object
15404          */
15405         OK : {ok:true},
15406         /**
15407          * Button config that displays Yes and No buttons
15408          * @type Object
15409          */
15410         YESNO : {yes:true, no:true},
15411         /**
15412          * Button config that displays OK and Cancel buttons
15413          * @type Object
15414          */
15415         OKCANCEL : {ok:true, cancel:true},
15416         /**
15417          * Button config that displays Yes, No and Cancel buttons
15418          * @type Object
15419          */
15420         YESNOCANCEL : {yes:true, no:true, cancel:true},
15421
15422         /**
15423          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15424          * @type Number
15425          */
15426         defaultTextHeight : 75,
15427         /**
15428          * The maximum width in pixels of the message box (defaults to 600)
15429          * @type Number
15430          */
15431         maxWidth : 600,
15432         /**
15433          * The minimum width in pixels of the message box (defaults to 100)
15434          * @type Number
15435          */
15436         minWidth : 100,
15437         /**
15438          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15439          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15440          * @type Number
15441          */
15442         minProgressWidth : 250,
15443         /**
15444          * An object containing the default button text strings that can be overriden for localized language support.
15445          * Supported properties are: ok, cancel, yes and no.
15446          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15447          * @type Object
15448          */
15449         buttonText : {
15450             ok : "OK",
15451             cancel : "Cancel",
15452             yes : "Yes",
15453             no : "No"
15454         }
15455     };
15456 }();
15457
15458 /**
15459  * Shorthand for {@link Roo.MessageBox}
15460  */
15461 Roo.Msg = Roo.MessageBox;/*
15462  * Based on:
15463  * Ext JS Library 1.1.1
15464  * Copyright(c) 2006-2007, Ext JS, LLC.
15465  *
15466  * Originally Released Under LGPL - original licence link has changed is not relivant.
15467  *
15468  * Fork - LGPL
15469  * <script type="text/javascript">
15470  */
15471 /**
15472  * @class Roo.QuickTips
15473  * Provides attractive and customizable tooltips for any element.
15474  * @singleton
15475  */
15476 Roo.QuickTips = function(){
15477     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15478     var ce, bd, xy, dd;
15479     var visible = false, disabled = true, inited = false;
15480     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15481     
15482     var onOver = function(e){
15483         if(disabled){
15484             return;
15485         }
15486         var t = e.getTarget();
15487         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15488             return;
15489         }
15490         if(ce && t == ce.el){
15491             clearTimeout(hideProc);
15492             return;
15493         }
15494         if(t && tagEls[t.id]){
15495             tagEls[t.id].el = t;
15496             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15497             return;
15498         }
15499         var ttp, et = Roo.fly(t);
15500         var ns = cfg.namespace;
15501         if(tm.interceptTitles && t.title){
15502             ttp = t.title;
15503             t.qtip = ttp;
15504             t.removeAttribute("title");
15505             e.preventDefault();
15506         }else{
15507             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15508         }
15509         if(ttp){
15510             showProc = show.defer(tm.showDelay, tm, [{
15511                 el: t, 
15512                 text: ttp, 
15513                 width: et.getAttributeNS(ns, cfg.width),
15514                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15515                 title: et.getAttributeNS(ns, cfg.title),
15516                     cls: et.getAttributeNS(ns, cfg.cls)
15517             }]);
15518         }
15519     };
15520     
15521     var onOut = function(e){
15522         clearTimeout(showProc);
15523         var t = e.getTarget();
15524         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15525             hideProc = setTimeout(hide, tm.hideDelay);
15526         }
15527     };
15528     
15529     var onMove = function(e){
15530         if(disabled){
15531             return;
15532         }
15533         xy = e.getXY();
15534         xy[1] += 18;
15535         if(tm.trackMouse && ce){
15536             el.setXY(xy);
15537         }
15538     };
15539     
15540     var onDown = function(e){
15541         clearTimeout(showProc);
15542         clearTimeout(hideProc);
15543         if(!e.within(el)){
15544             if(tm.hideOnClick){
15545                 hide();
15546                 tm.disable();
15547                 tm.enable.defer(100, tm);
15548             }
15549         }
15550     };
15551     
15552     var getPad = function(){
15553         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15554     };
15555
15556     var show = function(o){
15557         if(disabled){
15558             return;
15559         }
15560         clearTimeout(dismissProc);
15561         ce = o;
15562         if(removeCls){ // in case manually hidden
15563             el.removeClass(removeCls);
15564             removeCls = null;
15565         }
15566         if(ce.cls){
15567             el.addClass(ce.cls);
15568             removeCls = ce.cls;
15569         }
15570         if(ce.title){
15571             tipTitle.update(ce.title);
15572             tipTitle.show();
15573         }else{
15574             tipTitle.update('');
15575             tipTitle.hide();
15576         }
15577         el.dom.style.width  = tm.maxWidth+'px';
15578         //tipBody.dom.style.width = '';
15579         tipBodyText.update(o.text);
15580         var p = getPad(), w = ce.width;
15581         if(!w){
15582             var td = tipBodyText.dom;
15583             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15584             if(aw > tm.maxWidth){
15585                 w = tm.maxWidth;
15586             }else if(aw < tm.minWidth){
15587                 w = tm.minWidth;
15588             }else{
15589                 w = aw;
15590             }
15591         }
15592         //tipBody.setWidth(w);
15593         el.setWidth(parseInt(w, 10) + p);
15594         if(ce.autoHide === false){
15595             close.setDisplayed(true);
15596             if(dd){
15597                 dd.unlock();
15598             }
15599         }else{
15600             close.setDisplayed(false);
15601             if(dd){
15602                 dd.lock();
15603             }
15604         }
15605         if(xy){
15606             el.avoidY = xy[1]-18;
15607             el.setXY(xy);
15608         }
15609         if(tm.animate){
15610             el.setOpacity(.1);
15611             el.setStyle("visibility", "visible");
15612             el.fadeIn({callback: afterShow});
15613         }else{
15614             afterShow();
15615         }
15616     };
15617     
15618     var afterShow = function(){
15619         if(ce){
15620             el.show();
15621             esc.enable();
15622             if(tm.autoDismiss && ce.autoHide !== false){
15623                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15624             }
15625         }
15626     };
15627     
15628     var hide = function(noanim){
15629         clearTimeout(dismissProc);
15630         clearTimeout(hideProc);
15631         ce = null;
15632         if(el.isVisible()){
15633             esc.disable();
15634             if(noanim !== true && tm.animate){
15635                 el.fadeOut({callback: afterHide});
15636             }else{
15637                 afterHide();
15638             } 
15639         }
15640     };
15641     
15642     var afterHide = function(){
15643         el.hide();
15644         if(removeCls){
15645             el.removeClass(removeCls);
15646             removeCls = null;
15647         }
15648     };
15649     
15650     return {
15651         /**
15652         * @cfg {Number} minWidth
15653         * The minimum width of the quick tip (defaults to 40)
15654         */
15655        minWidth : 40,
15656         /**
15657         * @cfg {Number} maxWidth
15658         * The maximum width of the quick tip (defaults to 300)
15659         */
15660        maxWidth : 300,
15661         /**
15662         * @cfg {Boolean} interceptTitles
15663         * True to automatically use the element's DOM title value if available (defaults to false)
15664         */
15665        interceptTitles : false,
15666         /**
15667         * @cfg {Boolean} trackMouse
15668         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15669         */
15670        trackMouse : false,
15671         /**
15672         * @cfg {Boolean} hideOnClick
15673         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15674         */
15675        hideOnClick : true,
15676         /**
15677         * @cfg {Number} showDelay
15678         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15679         */
15680        showDelay : 500,
15681         /**
15682         * @cfg {Number} hideDelay
15683         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15684         */
15685        hideDelay : 200,
15686         /**
15687         * @cfg {Boolean} autoHide
15688         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15689         * Used in conjunction with hideDelay.
15690         */
15691        autoHide : true,
15692         /**
15693         * @cfg {Boolean}
15694         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15695         * (defaults to true).  Used in conjunction with autoDismissDelay.
15696         */
15697        autoDismiss : true,
15698         /**
15699         * @cfg {Number}
15700         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15701         */
15702        autoDismissDelay : 5000,
15703        /**
15704         * @cfg {Boolean} animate
15705         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15706         */
15707        animate : false,
15708
15709        /**
15710         * @cfg {String} title
15711         * Title text to display (defaults to '').  This can be any valid HTML markup.
15712         */
15713         title: '',
15714        /**
15715         * @cfg {String} text
15716         * Body text to display (defaults to '').  This can be any valid HTML markup.
15717         */
15718         text : '',
15719        /**
15720         * @cfg {String} cls
15721         * A CSS class to apply to the base quick tip element (defaults to '').
15722         */
15723         cls : '',
15724        /**
15725         * @cfg {Number} width
15726         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15727         * minWidth or maxWidth.
15728         */
15729         width : null,
15730
15731     /**
15732      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15733      * or display QuickTips in a page.
15734      */
15735        init : function(){
15736           tm = Roo.QuickTips;
15737           cfg = tm.tagConfig;
15738           if(!inited){
15739               if(!Roo.isReady){ // allow calling of init() before onReady
15740                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15741                   return;
15742               }
15743               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15744               el.fxDefaults = {stopFx: true};
15745               // maximum custom styling
15746               //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>');
15747               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>');              
15748               tipTitle = el.child('h3');
15749               tipTitle.enableDisplayMode("block");
15750               tipBody = el.child('div.x-tip-bd');
15751               tipBodyText = el.child('div.x-tip-bd-inner');
15752               //bdLeft = el.child('div.x-tip-bd-left');
15753               //bdRight = el.child('div.x-tip-bd-right');
15754               close = el.child('div.x-tip-close');
15755               close.enableDisplayMode("block");
15756               close.on("click", hide);
15757               var d = Roo.get(document);
15758               d.on("mousedown", onDown);
15759               d.on("mouseover", onOver);
15760               d.on("mouseout", onOut);
15761               d.on("mousemove", onMove);
15762               esc = d.addKeyListener(27, hide);
15763               esc.disable();
15764               if(Roo.dd.DD){
15765                   dd = el.initDD("default", null, {
15766                       onDrag : function(){
15767                           el.sync();  
15768                       }
15769                   });
15770                   dd.setHandleElId(tipTitle.id);
15771                   dd.lock();
15772               }
15773               inited = true;
15774           }
15775           this.enable(); 
15776        },
15777
15778     /**
15779      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15780      * are supported:
15781      * <pre>
15782 Property    Type                   Description
15783 ----------  ---------------------  ------------------------------------------------------------------------
15784 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15785      * </ul>
15786      * @param {Object} config The config object
15787      */
15788        register : function(config){
15789            var cs = config instanceof Array ? config : arguments;
15790            for(var i = 0, len = cs.length; i < len; i++) {
15791                var c = cs[i];
15792                var target = c.target;
15793                if(target){
15794                    if(target instanceof Array){
15795                        for(var j = 0, jlen = target.length; j < jlen; j++){
15796                            tagEls[target[j]] = c;
15797                        }
15798                    }else{
15799                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15800                    }
15801                }
15802            }
15803        },
15804
15805     /**
15806      * Removes this quick tip from its element and destroys it.
15807      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15808      */
15809        unregister : function(el){
15810            delete tagEls[Roo.id(el)];
15811        },
15812
15813     /**
15814      * Enable this quick tip.
15815      */
15816        enable : function(){
15817            if(inited && disabled){
15818                locks.pop();
15819                if(locks.length < 1){
15820                    disabled = false;
15821                }
15822            }
15823        },
15824
15825     /**
15826      * Disable this quick tip.
15827      */
15828        disable : function(){
15829           disabled = true;
15830           clearTimeout(showProc);
15831           clearTimeout(hideProc);
15832           clearTimeout(dismissProc);
15833           if(ce){
15834               hide(true);
15835           }
15836           locks.push(1);
15837        },
15838
15839     /**
15840      * Returns true if the quick tip is enabled, else false.
15841      */
15842        isEnabled : function(){
15843             return !disabled;
15844        },
15845
15846         // private
15847        tagConfig : {
15848            namespace : "ext",
15849            attribute : "qtip",
15850            width : "width",
15851            target : "target",
15852            title : "qtitle",
15853            hide : "hide",
15854            cls : "qclass"
15855        }
15856    };
15857 }();
15858
15859 // backwards compat
15860 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15861  * Based on:
15862  * Ext JS Library 1.1.1
15863  * Copyright(c) 2006-2007, Ext JS, LLC.
15864  *
15865  * Originally Released Under LGPL - original licence link has changed is not relivant.
15866  *
15867  * Fork - LGPL
15868  * <script type="text/javascript">
15869  */
15870  
15871
15872 /**
15873  * @class Roo.tree.TreePanel
15874  * @extends Roo.data.Tree
15875
15876  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15877  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15878  * @cfg {Boolean} enableDD true to enable drag and drop
15879  * @cfg {Boolean} enableDrag true to enable just drag
15880  * @cfg {Boolean} enableDrop true to enable just drop
15881  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15882  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15883  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15884  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15885  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15886  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15887  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15888  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15889  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15890  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15891  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15892  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15893  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15894  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15895  * @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>
15896  * @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>
15897  * 
15898  * @constructor
15899  * @param {String/HTMLElement/Element} el The container element
15900  * @param {Object} config
15901  */
15902 Roo.tree.TreePanel = function(el, config){
15903     var root = false;
15904     var loader = false;
15905     if (config.root) {
15906         root = config.root;
15907         delete config.root;
15908     }
15909     if (config.loader) {
15910         loader = config.loader;
15911         delete config.loader;
15912     }
15913     
15914     Roo.apply(this, config);
15915     Roo.tree.TreePanel.superclass.constructor.call(this);
15916     this.el = Roo.get(el);
15917     this.el.addClass('x-tree');
15918     //console.log(root);
15919     if (root) {
15920         this.setRootNode( Roo.factory(root, Roo.tree));
15921     }
15922     if (loader) {
15923         this.loader = Roo.factory(loader, Roo.tree);
15924     }
15925    /**
15926     * Read-only. The id of the container element becomes this TreePanel's id.
15927     */
15928     this.id = this.el.id;
15929     this.addEvents({
15930         /**
15931         * @event beforeload
15932         * Fires before a node is loaded, return false to cancel
15933         * @param {Node} node The node being loaded
15934         */
15935         "beforeload" : true,
15936         /**
15937         * @event load
15938         * Fires when a node is loaded
15939         * @param {Node} node The node that was loaded
15940         */
15941         "load" : true,
15942         /**
15943         * @event textchange
15944         * Fires when the text for a node is changed
15945         * @param {Node} node The node
15946         * @param {String} text The new text
15947         * @param {String} oldText The old text
15948         */
15949         "textchange" : true,
15950         /**
15951         * @event beforeexpand
15952         * Fires before a node is expanded, return false to cancel.
15953         * @param {Node} node The node
15954         * @param {Boolean} deep
15955         * @param {Boolean} anim
15956         */
15957         "beforeexpand" : true,
15958         /**
15959         * @event beforecollapse
15960         * Fires before a node is collapsed, return false to cancel.
15961         * @param {Node} node The node
15962         * @param {Boolean} deep
15963         * @param {Boolean} anim
15964         */
15965         "beforecollapse" : true,
15966         /**
15967         * @event expand
15968         * Fires when a node is expanded
15969         * @param {Node} node The node
15970         */
15971         "expand" : true,
15972         /**
15973         * @event disabledchange
15974         * Fires when the disabled status of a node changes
15975         * @param {Node} node The node
15976         * @param {Boolean} disabled
15977         */
15978         "disabledchange" : true,
15979         /**
15980         * @event collapse
15981         * Fires when a node is collapsed
15982         * @param {Node} node The node
15983         */
15984         "collapse" : true,
15985         /**
15986         * @event beforeclick
15987         * Fires before click processing on a node. Return false to cancel the default action.
15988         * @param {Node} node The node
15989         * @param {Roo.EventObject} e The event object
15990         */
15991         "beforeclick":true,
15992         /**
15993         * @event checkchange
15994         * Fires when a node with a checkbox's checked property changes
15995         * @param {Node} this This node
15996         * @param {Boolean} checked
15997         */
15998         "checkchange":true,
15999         /**
16000         * @event click
16001         * Fires when a node is clicked
16002         * @param {Node} node The node
16003         * @param {Roo.EventObject} e The event object
16004         */
16005         "click":true,
16006         /**
16007         * @event dblclick
16008         * Fires when a node is double clicked
16009         * @param {Node} node The node
16010         * @param {Roo.EventObject} e The event object
16011         */
16012         "dblclick":true,
16013         /**
16014         * @event contextmenu
16015         * Fires when a node is right clicked
16016         * @param {Node} node The node
16017         * @param {Roo.EventObject} e The event object
16018         */
16019         "contextmenu":true,
16020         /**
16021         * @event beforechildrenrendered
16022         * Fires right before the child nodes for a node are rendered
16023         * @param {Node} node The node
16024         */
16025         "beforechildrenrendered":true,
16026         /**
16027         * @event startdrag
16028         * Fires when a node starts being dragged
16029         * @param {Roo.tree.TreePanel} this
16030         * @param {Roo.tree.TreeNode} node
16031         * @param {event} e The raw browser event
16032         */ 
16033        "startdrag" : true,
16034        /**
16035         * @event enddrag
16036         * Fires when a drag operation is complete
16037         * @param {Roo.tree.TreePanel} this
16038         * @param {Roo.tree.TreeNode} node
16039         * @param {event} e The raw browser event
16040         */
16041        "enddrag" : true,
16042        /**
16043         * @event dragdrop
16044         * Fires when a dragged node is dropped on a valid DD target
16045         * @param {Roo.tree.TreePanel} this
16046         * @param {Roo.tree.TreeNode} node
16047         * @param {DD} dd The dd it was dropped on
16048         * @param {event} e The raw browser event
16049         */
16050        "dragdrop" : true,
16051        /**
16052         * @event beforenodedrop
16053         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16054         * passed to handlers has the following properties:<br />
16055         * <ul style="padding:5px;padding-left:16px;">
16056         * <li>tree - The TreePanel</li>
16057         * <li>target - The node being targeted for the drop</li>
16058         * <li>data - The drag data from the drag source</li>
16059         * <li>point - The point of the drop - append, above or below</li>
16060         * <li>source - The drag source</li>
16061         * <li>rawEvent - Raw mouse event</li>
16062         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16063         * to be inserted by setting them on this object.</li>
16064         * <li>cancel - Set this to true to cancel the drop.</li>
16065         * </ul>
16066         * @param {Object} dropEvent
16067         */
16068        "beforenodedrop" : true,
16069        /**
16070         * @event nodedrop
16071         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16072         * passed to handlers has the following properties:<br />
16073         * <ul style="padding:5px;padding-left:16px;">
16074         * <li>tree - The TreePanel</li>
16075         * <li>target - The node being targeted for the drop</li>
16076         * <li>data - The drag data from the drag source</li>
16077         * <li>point - The point of the drop - append, above or below</li>
16078         * <li>source - The drag source</li>
16079         * <li>rawEvent - Raw mouse event</li>
16080         * <li>dropNode - Dropped node(s).</li>
16081         * </ul>
16082         * @param {Object} dropEvent
16083         */
16084        "nodedrop" : true,
16085         /**
16086         * @event nodedragover
16087         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16088         * passed to handlers has the following properties:<br />
16089         * <ul style="padding:5px;padding-left:16px;">
16090         * <li>tree - The TreePanel</li>
16091         * <li>target - The node being targeted for the drop</li>
16092         * <li>data - The drag data from the drag source</li>
16093         * <li>point - The point of the drop - append, above or below</li>
16094         * <li>source - The drag source</li>
16095         * <li>rawEvent - Raw mouse event</li>
16096         * <li>dropNode - Drop node(s) provided by the source.</li>
16097         * <li>cancel - Set this to true to signal drop not allowed.</li>
16098         * </ul>
16099         * @param {Object} dragOverEvent
16100         */
16101        "nodedragover" : true
16102         
16103     });
16104     if(this.singleExpand){
16105        this.on("beforeexpand", this.restrictExpand, this);
16106     }
16107     if (this.editor) {
16108         this.editor.tree = this;
16109         this.editor = Roo.factory(this.editor, Roo.tree);
16110     }
16111     
16112     if (this.selModel) {
16113         this.selModel = Roo.factory(this.selModel, Roo.tree);
16114     }
16115    
16116 };
16117 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16118     rootVisible : true,
16119     animate: Roo.enableFx,
16120     lines : true,
16121     enableDD : false,
16122     hlDrop : Roo.enableFx,
16123   
16124     renderer: false,
16125     
16126     rendererTip: false,
16127     // private
16128     restrictExpand : function(node){
16129         var p = node.parentNode;
16130         if(p){
16131             if(p.expandedChild && p.expandedChild.parentNode == p){
16132                 p.expandedChild.collapse();
16133             }
16134             p.expandedChild = node;
16135         }
16136     },
16137
16138     // private override
16139     setRootNode : function(node){
16140         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16141         if(!this.rootVisible){
16142             node.ui = new Roo.tree.RootTreeNodeUI(node);
16143         }
16144         return node;
16145     },
16146
16147     /**
16148      * Returns the container element for this TreePanel
16149      */
16150     getEl : function(){
16151         return this.el;
16152     },
16153
16154     /**
16155      * Returns the default TreeLoader for this TreePanel
16156      */
16157     getLoader : function(){
16158         return this.loader;
16159     },
16160
16161     /**
16162      * Expand all nodes
16163      */
16164     expandAll : function(){
16165         this.root.expand(true);
16166     },
16167
16168     /**
16169      * Collapse all nodes
16170      */
16171     collapseAll : function(){
16172         this.root.collapse(true);
16173     },
16174
16175     /**
16176      * Returns the selection model used by this TreePanel
16177      */
16178     getSelectionModel : function(){
16179         if(!this.selModel){
16180             this.selModel = new Roo.tree.DefaultSelectionModel();
16181         }
16182         return this.selModel;
16183     },
16184
16185     /**
16186      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16187      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16188      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16189      * @return {Array}
16190      */
16191     getChecked : function(a, startNode){
16192         startNode = startNode || this.root;
16193         var r = [];
16194         var f = function(){
16195             if(this.attributes.checked){
16196                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16197             }
16198         }
16199         startNode.cascade(f);
16200         return r;
16201     },
16202
16203     /**
16204      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16205      * @param {String} path
16206      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16207      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16208      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16209      */
16210     expandPath : function(path, attr, callback){
16211         attr = attr || "id";
16212         var keys = path.split(this.pathSeparator);
16213         var curNode = this.root;
16214         if(curNode.attributes[attr] != keys[1]){ // invalid root
16215             if(callback){
16216                 callback(false, null);
16217             }
16218             return;
16219         }
16220         var index = 1;
16221         var f = function(){
16222             if(++index == keys.length){
16223                 if(callback){
16224                     callback(true, curNode);
16225                 }
16226                 return;
16227             }
16228             var c = curNode.findChild(attr, keys[index]);
16229             if(!c){
16230                 if(callback){
16231                     callback(false, curNode);
16232                 }
16233                 return;
16234             }
16235             curNode = c;
16236             c.expand(false, false, f);
16237         };
16238         curNode.expand(false, false, f);
16239     },
16240
16241     /**
16242      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16243      * @param {String} path
16244      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16245      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16246      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16247      */
16248     selectPath : function(path, attr, callback){
16249         attr = attr || "id";
16250         var keys = path.split(this.pathSeparator);
16251         var v = keys.pop();
16252         if(keys.length > 0){
16253             var f = function(success, node){
16254                 if(success && node){
16255                     var n = node.findChild(attr, v);
16256                     if(n){
16257                         n.select();
16258                         if(callback){
16259                             callback(true, n);
16260                         }
16261                     }else if(callback){
16262                         callback(false, n);
16263                     }
16264                 }else{
16265                     if(callback){
16266                         callback(false, n);
16267                     }
16268                 }
16269             };
16270             this.expandPath(keys.join(this.pathSeparator), attr, f);
16271         }else{
16272             this.root.select();
16273             if(callback){
16274                 callback(true, this.root);
16275             }
16276         }
16277     },
16278
16279     getTreeEl : function(){
16280         return this.el;
16281     },
16282
16283     /**
16284      * Trigger rendering of this TreePanel
16285      */
16286     render : function(){
16287         if (this.innerCt) {
16288             return this; // stop it rendering more than once!!
16289         }
16290         
16291         this.innerCt = this.el.createChild({tag:"ul",
16292                cls:"x-tree-root-ct " +
16293                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16294
16295         if(this.containerScroll){
16296             Roo.dd.ScrollManager.register(this.el);
16297         }
16298         if((this.enableDD || this.enableDrop) && !this.dropZone){
16299            /**
16300             * The dropZone used by this tree if drop is enabled
16301             * @type Roo.tree.TreeDropZone
16302             */
16303              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16304                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16305            });
16306         }
16307         if((this.enableDD || this.enableDrag) && !this.dragZone){
16308            /**
16309             * The dragZone used by this tree if drag is enabled
16310             * @type Roo.tree.TreeDragZone
16311             */
16312             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16313                ddGroup: this.ddGroup || "TreeDD",
16314                scroll: this.ddScroll
16315            });
16316         }
16317         this.getSelectionModel().init(this);
16318         if (!this.root) {
16319             Roo.log("ROOT not set in tree");
16320             return this;
16321         }
16322         this.root.render();
16323         if(!this.rootVisible){
16324             this.root.renderChildren();
16325         }
16326         return this;
16327     }
16328 });/*
16329  * Based on:
16330  * Ext JS Library 1.1.1
16331  * Copyright(c) 2006-2007, Ext JS, LLC.
16332  *
16333  * Originally Released Under LGPL - original licence link has changed is not relivant.
16334  *
16335  * Fork - LGPL
16336  * <script type="text/javascript">
16337  */
16338  
16339
16340 /**
16341  * @class Roo.tree.DefaultSelectionModel
16342  * @extends Roo.util.Observable
16343  * The default single selection for a TreePanel.
16344  * @param {Object} cfg Configuration
16345  */
16346 Roo.tree.DefaultSelectionModel = function(cfg){
16347    this.selNode = null;
16348    
16349    
16350    
16351    this.addEvents({
16352        /**
16353         * @event selectionchange
16354         * Fires when the selected node changes
16355         * @param {DefaultSelectionModel} this
16356         * @param {TreeNode} node the new selection
16357         */
16358        "selectionchange" : true,
16359
16360        /**
16361         * @event beforeselect
16362         * Fires before the selected node changes, return false to cancel the change
16363         * @param {DefaultSelectionModel} this
16364         * @param {TreeNode} node the new selection
16365         * @param {TreeNode} node the old selection
16366         */
16367        "beforeselect" : true
16368    });
16369    
16370     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16371 };
16372
16373 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16374     init : function(tree){
16375         this.tree = tree;
16376         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16377         tree.on("click", this.onNodeClick, this);
16378     },
16379     
16380     onNodeClick : function(node, e){
16381         if (e.ctrlKey && this.selNode == node)  {
16382             this.unselect(node);
16383             return;
16384         }
16385         this.select(node);
16386     },
16387     
16388     /**
16389      * Select a node.
16390      * @param {TreeNode} node The node to select
16391      * @return {TreeNode} The selected node
16392      */
16393     select : function(node){
16394         var last = this.selNode;
16395         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16396             if(last){
16397                 last.ui.onSelectedChange(false);
16398             }
16399             this.selNode = node;
16400             node.ui.onSelectedChange(true);
16401             this.fireEvent("selectionchange", this, node, last);
16402         }
16403         return node;
16404     },
16405     
16406     /**
16407      * Deselect a node.
16408      * @param {TreeNode} node The node to unselect
16409      */
16410     unselect : function(node){
16411         if(this.selNode == node){
16412             this.clearSelections();
16413         }    
16414     },
16415     
16416     /**
16417      * Clear all selections
16418      */
16419     clearSelections : function(){
16420         var n = this.selNode;
16421         if(n){
16422             n.ui.onSelectedChange(false);
16423             this.selNode = null;
16424             this.fireEvent("selectionchange", this, null);
16425         }
16426         return n;
16427     },
16428     
16429     /**
16430      * Get the selected node
16431      * @return {TreeNode} The selected node
16432      */
16433     getSelectedNode : function(){
16434         return this.selNode;    
16435     },
16436     
16437     /**
16438      * Returns true if the node is selected
16439      * @param {TreeNode} node The node to check
16440      * @return {Boolean}
16441      */
16442     isSelected : function(node){
16443         return this.selNode == node;  
16444     },
16445
16446     /**
16447      * Selects the node above the selected node in the tree, intelligently walking the nodes
16448      * @return TreeNode The new selection
16449      */
16450     selectPrevious : function(){
16451         var s = this.selNode || this.lastSelNode;
16452         if(!s){
16453             return null;
16454         }
16455         var ps = s.previousSibling;
16456         if(ps){
16457             if(!ps.isExpanded() || ps.childNodes.length < 1){
16458                 return this.select(ps);
16459             } else{
16460                 var lc = ps.lastChild;
16461                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16462                     lc = lc.lastChild;
16463                 }
16464                 return this.select(lc);
16465             }
16466         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16467             return this.select(s.parentNode);
16468         }
16469         return null;
16470     },
16471
16472     /**
16473      * Selects the node above the selected node in the tree, intelligently walking the nodes
16474      * @return TreeNode The new selection
16475      */
16476     selectNext : function(){
16477         var s = this.selNode || this.lastSelNode;
16478         if(!s){
16479             return null;
16480         }
16481         if(s.firstChild && s.isExpanded()){
16482              return this.select(s.firstChild);
16483          }else if(s.nextSibling){
16484              return this.select(s.nextSibling);
16485          }else if(s.parentNode){
16486             var newS = null;
16487             s.parentNode.bubble(function(){
16488                 if(this.nextSibling){
16489                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16490                     return false;
16491                 }
16492             });
16493             return newS;
16494          }
16495         return null;
16496     },
16497
16498     onKeyDown : function(e){
16499         var s = this.selNode || this.lastSelNode;
16500         // undesirable, but required
16501         var sm = this;
16502         if(!s){
16503             return;
16504         }
16505         var k = e.getKey();
16506         switch(k){
16507              case e.DOWN:
16508                  e.stopEvent();
16509                  this.selectNext();
16510              break;
16511              case e.UP:
16512                  e.stopEvent();
16513                  this.selectPrevious();
16514              break;
16515              case e.RIGHT:
16516                  e.preventDefault();
16517                  if(s.hasChildNodes()){
16518                      if(!s.isExpanded()){
16519                          s.expand();
16520                      }else if(s.firstChild){
16521                          this.select(s.firstChild, e);
16522                      }
16523                  }
16524              break;
16525              case e.LEFT:
16526                  e.preventDefault();
16527                  if(s.hasChildNodes() && s.isExpanded()){
16528                      s.collapse();
16529                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16530                      this.select(s.parentNode, e);
16531                  }
16532              break;
16533         };
16534     }
16535 });
16536
16537 /**
16538  * @class Roo.tree.MultiSelectionModel
16539  * @extends Roo.util.Observable
16540  * Multi selection for a TreePanel.
16541  * @param {Object} cfg Configuration
16542  */
16543 Roo.tree.MultiSelectionModel = function(){
16544    this.selNodes = [];
16545    this.selMap = {};
16546    this.addEvents({
16547        /**
16548         * @event selectionchange
16549         * Fires when the selected nodes change
16550         * @param {MultiSelectionModel} this
16551         * @param {Array} nodes Array of the selected nodes
16552         */
16553        "selectionchange" : true
16554    });
16555    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16556    
16557 };
16558
16559 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16560     init : function(tree){
16561         this.tree = tree;
16562         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16563         tree.on("click", this.onNodeClick, this);
16564     },
16565     
16566     onNodeClick : function(node, e){
16567         this.select(node, e, e.ctrlKey);
16568     },
16569     
16570     /**
16571      * Select a node.
16572      * @param {TreeNode} node The node to select
16573      * @param {EventObject} e (optional) An event associated with the selection
16574      * @param {Boolean} keepExisting True to retain existing selections
16575      * @return {TreeNode} The selected node
16576      */
16577     select : function(node, e, keepExisting){
16578         if(keepExisting !== true){
16579             this.clearSelections(true);
16580         }
16581         if(this.isSelected(node)){
16582             this.lastSelNode = node;
16583             return node;
16584         }
16585         this.selNodes.push(node);
16586         this.selMap[node.id] = node;
16587         this.lastSelNode = node;
16588         node.ui.onSelectedChange(true);
16589         this.fireEvent("selectionchange", this, this.selNodes);
16590         return node;
16591     },
16592     
16593     /**
16594      * Deselect a node.
16595      * @param {TreeNode} node The node to unselect
16596      */
16597     unselect : function(node){
16598         if(this.selMap[node.id]){
16599             node.ui.onSelectedChange(false);
16600             var sn = this.selNodes;
16601             var index = -1;
16602             if(sn.indexOf){
16603                 index = sn.indexOf(node);
16604             }else{
16605                 for(var i = 0, len = sn.length; i < len; i++){
16606                     if(sn[i] == node){
16607                         index = i;
16608                         break;
16609                     }
16610                 }
16611             }
16612             if(index != -1){
16613                 this.selNodes.splice(index, 1);
16614             }
16615             delete this.selMap[node.id];
16616             this.fireEvent("selectionchange", this, this.selNodes);
16617         }
16618     },
16619     
16620     /**
16621      * Clear all selections
16622      */
16623     clearSelections : function(suppressEvent){
16624         var sn = this.selNodes;
16625         if(sn.length > 0){
16626             for(var i = 0, len = sn.length; i < len; i++){
16627                 sn[i].ui.onSelectedChange(false);
16628             }
16629             this.selNodes = [];
16630             this.selMap = {};
16631             if(suppressEvent !== true){
16632                 this.fireEvent("selectionchange", this, this.selNodes);
16633             }
16634         }
16635     },
16636     
16637     /**
16638      * Returns true if the node is selected
16639      * @param {TreeNode} node The node to check
16640      * @return {Boolean}
16641      */
16642     isSelected : function(node){
16643         return this.selMap[node.id] ? true : false;  
16644     },
16645     
16646     /**
16647      * Returns an array of the selected nodes
16648      * @return {Array}
16649      */
16650     getSelectedNodes : function(){
16651         return this.selNodes;    
16652     },
16653
16654     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16655
16656     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16657
16658     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16659 });/*
16660  * Based on:
16661  * Ext JS Library 1.1.1
16662  * Copyright(c) 2006-2007, Ext JS, LLC.
16663  *
16664  * Originally Released Under LGPL - original licence link has changed is not relivant.
16665  *
16666  * Fork - LGPL
16667  * <script type="text/javascript">
16668  */
16669  
16670 /**
16671  * @class Roo.tree.TreeNode
16672  * @extends Roo.data.Node
16673  * @cfg {String} text The text for this node
16674  * @cfg {Boolean} expanded true to start the node expanded
16675  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16676  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16677  * @cfg {Boolean} disabled true to start the node disabled
16678  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16679  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16680  * @cfg {String} cls A css class to be added to the node
16681  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16682  * @cfg {String} href URL of the link used for the node (defaults to #)
16683  * @cfg {String} hrefTarget target frame for the link
16684  * @cfg {String} qtip An Ext QuickTip for the node
16685  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16686  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16687  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16688  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16689  * (defaults to undefined with no checkbox rendered)
16690  * @constructor
16691  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16692  */
16693 Roo.tree.TreeNode = function(attributes){
16694     attributes = attributes || {};
16695     if(typeof attributes == "string"){
16696         attributes = {text: attributes};
16697     }
16698     this.childrenRendered = false;
16699     this.rendered = false;
16700     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16701     this.expanded = attributes.expanded === true;
16702     this.isTarget = attributes.isTarget !== false;
16703     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16704     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16705
16706     /**
16707      * Read-only. The text for this node. To change it use setText().
16708      * @type String
16709      */
16710     this.text = attributes.text;
16711     /**
16712      * True if this node is disabled.
16713      * @type Boolean
16714      */
16715     this.disabled = attributes.disabled === true;
16716
16717     this.addEvents({
16718         /**
16719         * @event textchange
16720         * Fires when the text for this node is changed
16721         * @param {Node} this This node
16722         * @param {String} text The new text
16723         * @param {String} oldText The old text
16724         */
16725         "textchange" : true,
16726         /**
16727         * @event beforeexpand
16728         * Fires before this node is expanded, return false to cancel.
16729         * @param {Node} this This node
16730         * @param {Boolean} deep
16731         * @param {Boolean} anim
16732         */
16733         "beforeexpand" : true,
16734         /**
16735         * @event beforecollapse
16736         * Fires before this node is collapsed, return false to cancel.
16737         * @param {Node} this This node
16738         * @param {Boolean} deep
16739         * @param {Boolean} anim
16740         */
16741         "beforecollapse" : true,
16742         /**
16743         * @event expand
16744         * Fires when this node is expanded
16745         * @param {Node} this This node
16746         */
16747         "expand" : true,
16748         /**
16749         * @event disabledchange
16750         * Fires when the disabled status of this node changes
16751         * @param {Node} this This node
16752         * @param {Boolean} disabled
16753         */
16754         "disabledchange" : true,
16755         /**
16756         * @event collapse
16757         * Fires when this node is collapsed
16758         * @param {Node} this This node
16759         */
16760         "collapse" : true,
16761         /**
16762         * @event beforeclick
16763         * Fires before click processing. Return false to cancel the default action.
16764         * @param {Node} this This node
16765         * @param {Roo.EventObject} e The event object
16766         */
16767         "beforeclick":true,
16768         /**
16769         * @event checkchange
16770         * Fires when a node with a checkbox's checked property changes
16771         * @param {Node} this This node
16772         * @param {Boolean} checked
16773         */
16774         "checkchange":true,
16775         /**
16776         * @event click
16777         * Fires when this node is clicked
16778         * @param {Node} this This node
16779         * @param {Roo.EventObject} e The event object
16780         */
16781         "click":true,
16782         /**
16783         * @event dblclick
16784         * Fires when this node is double clicked
16785         * @param {Node} this This node
16786         * @param {Roo.EventObject} e The event object
16787         */
16788         "dblclick":true,
16789         /**
16790         * @event contextmenu
16791         * Fires when this node is right clicked
16792         * @param {Node} this This node
16793         * @param {Roo.EventObject} e The event object
16794         */
16795         "contextmenu":true,
16796         /**
16797         * @event beforechildrenrendered
16798         * Fires right before the child nodes for this node are rendered
16799         * @param {Node} this This node
16800         */
16801         "beforechildrenrendered":true
16802     });
16803
16804     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16805
16806     /**
16807      * Read-only. The UI for this node
16808      * @type TreeNodeUI
16809      */
16810     this.ui = new uiClass(this);
16811     
16812     // finally support items[]
16813     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16814         return;
16815     }
16816     
16817     
16818     Roo.each(this.attributes.items, function(c) {
16819         this.appendChild(Roo.factory(c,Roo.Tree));
16820     }, this);
16821     delete this.attributes.items;
16822     
16823     
16824     
16825 };
16826 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16827     preventHScroll: true,
16828     /**
16829      * Returns true if this node is expanded
16830      * @return {Boolean}
16831      */
16832     isExpanded : function(){
16833         return this.expanded;
16834     },
16835
16836     /**
16837      * Returns the UI object for this node
16838      * @return {TreeNodeUI}
16839      */
16840     getUI : function(){
16841         return this.ui;
16842     },
16843
16844     // private override
16845     setFirstChild : function(node){
16846         var of = this.firstChild;
16847         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16848         if(this.childrenRendered && of && node != of){
16849             of.renderIndent(true, true);
16850         }
16851         if(this.rendered){
16852             this.renderIndent(true, true);
16853         }
16854     },
16855
16856     // private override
16857     setLastChild : function(node){
16858         var ol = this.lastChild;
16859         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16860         if(this.childrenRendered && ol && node != ol){
16861             ol.renderIndent(true, true);
16862         }
16863         if(this.rendered){
16864             this.renderIndent(true, true);
16865         }
16866     },
16867
16868     // these methods are overridden to provide lazy rendering support
16869     // private override
16870     appendChild : function()
16871     {
16872         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16873         if(node && this.childrenRendered){
16874             node.render();
16875         }
16876         this.ui.updateExpandIcon();
16877         return node;
16878     },
16879
16880     // private override
16881     removeChild : function(node){
16882         this.ownerTree.getSelectionModel().unselect(node);
16883         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16884         // if it's been rendered remove dom node
16885         if(this.childrenRendered){
16886             node.ui.remove();
16887         }
16888         if(this.childNodes.length < 1){
16889             this.collapse(false, false);
16890         }else{
16891             this.ui.updateExpandIcon();
16892         }
16893         if(!this.firstChild) {
16894             this.childrenRendered = false;
16895         }
16896         return node;
16897     },
16898
16899     // private override
16900     insertBefore : function(node, refNode){
16901         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16902         if(newNode && refNode && this.childrenRendered){
16903             node.render();
16904         }
16905         this.ui.updateExpandIcon();
16906         return newNode;
16907     },
16908
16909     /**
16910      * Sets the text for this node
16911      * @param {String} text
16912      */
16913     setText : function(text){
16914         var oldText = this.text;
16915         this.text = text;
16916         this.attributes.text = text;
16917         if(this.rendered){ // event without subscribing
16918             this.ui.onTextChange(this, text, oldText);
16919         }
16920         this.fireEvent("textchange", this, text, oldText);
16921     },
16922
16923     /**
16924      * Triggers selection of this node
16925      */
16926     select : function(){
16927         this.getOwnerTree().getSelectionModel().select(this);
16928     },
16929
16930     /**
16931      * Triggers deselection of this node
16932      */
16933     unselect : function(){
16934         this.getOwnerTree().getSelectionModel().unselect(this);
16935     },
16936
16937     /**
16938      * Returns true if this node is selected
16939      * @return {Boolean}
16940      */
16941     isSelected : function(){
16942         return this.getOwnerTree().getSelectionModel().isSelected(this);
16943     },
16944
16945     /**
16946      * Expand this node.
16947      * @param {Boolean} deep (optional) True to expand all children as well
16948      * @param {Boolean} anim (optional) false to cancel the default animation
16949      * @param {Function} callback (optional) A callback to be called when
16950      * expanding this node completes (does not wait for deep expand to complete).
16951      * Called with 1 parameter, this node.
16952      */
16953     expand : function(deep, anim, callback){
16954         if(!this.expanded){
16955             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
16956                 return;
16957             }
16958             if(!this.childrenRendered){
16959                 this.renderChildren();
16960             }
16961             this.expanded = true;
16962             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
16963                 this.ui.animExpand(function(){
16964                     this.fireEvent("expand", this);
16965                     if(typeof callback == "function"){
16966                         callback(this);
16967                     }
16968                     if(deep === true){
16969                         this.expandChildNodes(true);
16970                     }
16971                 }.createDelegate(this));
16972                 return;
16973             }else{
16974                 this.ui.expand();
16975                 this.fireEvent("expand", this);
16976                 if(typeof callback == "function"){
16977                     callback(this);
16978                 }
16979             }
16980         }else{
16981            if(typeof callback == "function"){
16982                callback(this);
16983            }
16984         }
16985         if(deep === true){
16986             this.expandChildNodes(true);
16987         }
16988     },
16989
16990     isHiddenRoot : function(){
16991         return this.isRoot && !this.getOwnerTree().rootVisible;
16992     },
16993
16994     /**
16995      * Collapse this node.
16996      * @param {Boolean} deep (optional) True to collapse all children as well
16997      * @param {Boolean} anim (optional) false to cancel the default animation
16998      */
16999     collapse : function(deep, anim){
17000         if(this.expanded && !this.isHiddenRoot()){
17001             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17002                 return;
17003             }
17004             this.expanded = false;
17005             if((this.getOwnerTree().animate && anim !== false) || anim){
17006                 this.ui.animCollapse(function(){
17007                     this.fireEvent("collapse", this);
17008                     if(deep === true){
17009                         this.collapseChildNodes(true);
17010                     }
17011                 }.createDelegate(this));
17012                 return;
17013             }else{
17014                 this.ui.collapse();
17015                 this.fireEvent("collapse", this);
17016             }
17017         }
17018         if(deep === true){
17019             var cs = this.childNodes;
17020             for(var i = 0, len = cs.length; i < len; i++) {
17021                 cs[i].collapse(true, false);
17022             }
17023         }
17024     },
17025
17026     // private
17027     delayedExpand : function(delay){
17028         if(!this.expandProcId){
17029             this.expandProcId = this.expand.defer(delay, this);
17030         }
17031     },
17032
17033     // private
17034     cancelExpand : function(){
17035         if(this.expandProcId){
17036             clearTimeout(this.expandProcId);
17037         }
17038         this.expandProcId = false;
17039     },
17040
17041     /**
17042      * Toggles expanded/collapsed state of the node
17043      */
17044     toggle : function(){
17045         if(this.expanded){
17046             this.collapse();
17047         }else{
17048             this.expand();
17049         }
17050     },
17051
17052     /**
17053      * Ensures all parent nodes are expanded
17054      */
17055     ensureVisible : function(callback){
17056         var tree = this.getOwnerTree();
17057         tree.expandPath(this.parentNode.getPath(), false, function(){
17058             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17059             Roo.callback(callback);
17060         }.createDelegate(this));
17061     },
17062
17063     /**
17064      * Expand all child nodes
17065      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17066      */
17067     expandChildNodes : function(deep){
17068         var cs = this.childNodes;
17069         for(var i = 0, len = cs.length; i < len; i++) {
17070                 cs[i].expand(deep);
17071         }
17072     },
17073
17074     /**
17075      * Collapse all child nodes
17076      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17077      */
17078     collapseChildNodes : function(deep){
17079         var cs = this.childNodes;
17080         for(var i = 0, len = cs.length; i < len; i++) {
17081                 cs[i].collapse(deep);
17082         }
17083     },
17084
17085     /**
17086      * Disables this node
17087      */
17088     disable : function(){
17089         this.disabled = true;
17090         this.unselect();
17091         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17092             this.ui.onDisableChange(this, true);
17093         }
17094         this.fireEvent("disabledchange", this, true);
17095     },
17096
17097     /**
17098      * Enables this node
17099      */
17100     enable : function(){
17101         this.disabled = false;
17102         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17103             this.ui.onDisableChange(this, false);
17104         }
17105         this.fireEvent("disabledchange", this, false);
17106     },
17107
17108     // private
17109     renderChildren : function(suppressEvent){
17110         if(suppressEvent !== false){
17111             this.fireEvent("beforechildrenrendered", this);
17112         }
17113         var cs = this.childNodes;
17114         for(var i = 0, len = cs.length; i < len; i++){
17115             cs[i].render(true);
17116         }
17117         this.childrenRendered = true;
17118     },
17119
17120     // private
17121     sort : function(fn, scope){
17122         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17123         if(this.childrenRendered){
17124             var cs = this.childNodes;
17125             for(var i = 0, len = cs.length; i < len; i++){
17126                 cs[i].render(true);
17127             }
17128         }
17129     },
17130
17131     // private
17132     render : function(bulkRender){
17133         this.ui.render(bulkRender);
17134         if(!this.rendered){
17135             this.rendered = true;
17136             if(this.expanded){
17137                 this.expanded = false;
17138                 this.expand(false, false);
17139             }
17140         }
17141     },
17142
17143     // private
17144     renderIndent : function(deep, refresh){
17145         if(refresh){
17146             this.ui.childIndent = null;
17147         }
17148         this.ui.renderIndent();
17149         if(deep === true && this.childrenRendered){
17150             var cs = this.childNodes;
17151             for(var i = 0, len = cs.length; i < len; i++){
17152                 cs[i].renderIndent(true, refresh);
17153             }
17154         }
17155     }
17156 });/*
17157  * Based on:
17158  * Ext JS Library 1.1.1
17159  * Copyright(c) 2006-2007, Ext JS, LLC.
17160  *
17161  * Originally Released Under LGPL - original licence link has changed is not relivant.
17162  *
17163  * Fork - LGPL
17164  * <script type="text/javascript">
17165  */
17166  
17167 /**
17168  * @class Roo.tree.AsyncTreeNode
17169  * @extends Roo.tree.TreeNode
17170  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17171  * @constructor
17172  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17173  */
17174  Roo.tree.AsyncTreeNode = function(config){
17175     this.loaded = false;
17176     this.loading = false;
17177     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17178     /**
17179     * @event beforeload
17180     * Fires before this node is loaded, return false to cancel
17181     * @param {Node} this This node
17182     */
17183     this.addEvents({'beforeload':true, 'load': true});
17184     /**
17185     * @event load
17186     * Fires when this node is loaded
17187     * @param {Node} this This node
17188     */
17189     /**
17190      * The loader used by this node (defaults to using the tree's defined loader)
17191      * @type TreeLoader
17192      * @property loader
17193      */
17194 };
17195 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17196     expand : function(deep, anim, callback){
17197         if(this.loading){ // if an async load is already running, waiting til it's done
17198             var timer;
17199             var f = function(){
17200                 if(!this.loading){ // done loading
17201                     clearInterval(timer);
17202                     this.expand(deep, anim, callback);
17203                 }
17204             }.createDelegate(this);
17205             timer = setInterval(f, 200);
17206             return;
17207         }
17208         if(!this.loaded){
17209             if(this.fireEvent("beforeload", this) === false){
17210                 return;
17211             }
17212             this.loading = true;
17213             this.ui.beforeLoad(this);
17214             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17215             if(loader){
17216                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17217                 return;
17218             }
17219         }
17220         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17221     },
17222     
17223     /**
17224      * Returns true if this node is currently loading
17225      * @return {Boolean}
17226      */
17227     isLoading : function(){
17228         return this.loading;  
17229     },
17230     
17231     loadComplete : function(deep, anim, callback){
17232         this.loading = false;
17233         this.loaded = true;
17234         this.ui.afterLoad(this);
17235         this.fireEvent("load", this);
17236         this.expand(deep, anim, callback);
17237     },
17238     
17239     /**
17240      * Returns true if this node has been loaded
17241      * @return {Boolean}
17242      */
17243     isLoaded : function(){
17244         return this.loaded;
17245     },
17246     
17247     hasChildNodes : function(){
17248         if(!this.isLeaf() && !this.loaded){
17249             return true;
17250         }else{
17251             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17252         }
17253     },
17254
17255     /**
17256      * Trigger a reload for this node
17257      * @param {Function} callback
17258      */
17259     reload : function(callback){
17260         this.collapse(false, false);
17261         while(this.firstChild){
17262             this.removeChild(this.firstChild);
17263         }
17264         this.childrenRendered = false;
17265         this.loaded = false;
17266         if(this.isHiddenRoot()){
17267             this.expanded = false;
17268         }
17269         this.expand(false, false, callback);
17270     }
17271 });/*
17272  * Based on:
17273  * Ext JS Library 1.1.1
17274  * Copyright(c) 2006-2007, Ext JS, LLC.
17275  *
17276  * Originally Released Under LGPL - original licence link has changed is not relivant.
17277  *
17278  * Fork - LGPL
17279  * <script type="text/javascript">
17280  */
17281  
17282 /**
17283  * @class Roo.tree.TreeNodeUI
17284  * @constructor
17285  * @param {Object} node The node to render
17286  * The TreeNode UI implementation is separate from the
17287  * tree implementation. Unless you are customizing the tree UI,
17288  * you should never have to use this directly.
17289  */
17290 Roo.tree.TreeNodeUI = function(node){
17291     this.node = node;
17292     this.rendered = false;
17293     this.animating = false;
17294     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17295 };
17296
17297 Roo.tree.TreeNodeUI.prototype = {
17298     removeChild : function(node){
17299         if(this.rendered){
17300             this.ctNode.removeChild(node.ui.getEl());
17301         }
17302     },
17303
17304     beforeLoad : function(){
17305          this.addClass("x-tree-node-loading");
17306     },
17307
17308     afterLoad : function(){
17309          this.removeClass("x-tree-node-loading");
17310     },
17311
17312     onTextChange : function(node, text, oldText){
17313         if(this.rendered){
17314             this.textNode.innerHTML = text;
17315         }
17316     },
17317
17318     onDisableChange : function(node, state){
17319         this.disabled = state;
17320         if(state){
17321             this.addClass("x-tree-node-disabled");
17322         }else{
17323             this.removeClass("x-tree-node-disabled");
17324         }
17325     },
17326
17327     onSelectedChange : function(state){
17328         if(state){
17329             this.focus();
17330             this.addClass("x-tree-selected");
17331         }else{
17332             //this.blur();
17333             this.removeClass("x-tree-selected");
17334         }
17335     },
17336
17337     onMove : function(tree, node, oldParent, newParent, index, refNode){
17338         this.childIndent = null;
17339         if(this.rendered){
17340             var targetNode = newParent.ui.getContainer();
17341             if(!targetNode){//target not rendered
17342                 this.holder = document.createElement("div");
17343                 this.holder.appendChild(this.wrap);
17344                 return;
17345             }
17346             var insertBefore = refNode ? refNode.ui.getEl() : null;
17347             if(insertBefore){
17348                 targetNode.insertBefore(this.wrap, insertBefore);
17349             }else{
17350                 targetNode.appendChild(this.wrap);
17351             }
17352             this.node.renderIndent(true);
17353         }
17354     },
17355
17356     addClass : function(cls){
17357         if(this.elNode){
17358             Roo.fly(this.elNode).addClass(cls);
17359         }
17360     },
17361
17362     removeClass : function(cls){
17363         if(this.elNode){
17364             Roo.fly(this.elNode).removeClass(cls);
17365         }
17366     },
17367
17368     remove : function(){
17369         if(this.rendered){
17370             this.holder = document.createElement("div");
17371             this.holder.appendChild(this.wrap);
17372         }
17373     },
17374
17375     fireEvent : function(){
17376         return this.node.fireEvent.apply(this.node, arguments);
17377     },
17378
17379     initEvents : function(){
17380         this.node.on("move", this.onMove, this);
17381         var E = Roo.EventManager;
17382         var a = this.anchor;
17383
17384         var el = Roo.fly(a, '_treeui');
17385
17386         if(Roo.isOpera){ // opera render bug ignores the CSS
17387             el.setStyle("text-decoration", "none");
17388         }
17389
17390         el.on("click", this.onClick, this);
17391         el.on("dblclick", this.onDblClick, this);
17392
17393         if(this.checkbox){
17394             Roo.EventManager.on(this.checkbox,
17395                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17396         }
17397
17398         el.on("contextmenu", this.onContextMenu, this);
17399
17400         var icon = Roo.fly(this.iconNode);
17401         icon.on("click", this.onClick, this);
17402         icon.on("dblclick", this.onDblClick, this);
17403         icon.on("contextmenu", this.onContextMenu, this);
17404         E.on(this.ecNode, "click", this.ecClick, this, true);
17405
17406         if(this.node.disabled){
17407             this.addClass("x-tree-node-disabled");
17408         }
17409         if(this.node.hidden){
17410             this.addClass("x-tree-node-disabled");
17411         }
17412         var ot = this.node.getOwnerTree();
17413         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17414         if(dd && (!this.node.isRoot || ot.rootVisible)){
17415             Roo.dd.Registry.register(this.elNode, {
17416                 node: this.node,
17417                 handles: this.getDDHandles(),
17418                 isHandle: false
17419             });
17420         }
17421     },
17422
17423     getDDHandles : function(){
17424         return [this.iconNode, this.textNode];
17425     },
17426
17427     hide : function(){
17428         if(this.rendered){
17429             this.wrap.style.display = "none";
17430         }
17431     },
17432
17433     show : function(){
17434         if(this.rendered){
17435             this.wrap.style.display = "";
17436         }
17437     },
17438
17439     onContextMenu : function(e){
17440         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17441             e.preventDefault();
17442             this.focus();
17443             this.fireEvent("contextmenu", this.node, e);
17444         }
17445     },
17446
17447     onClick : function(e){
17448         if(this.dropping){
17449             e.stopEvent();
17450             return;
17451         }
17452         if(this.fireEvent("beforeclick", this.node, e) !== false){
17453             if(!this.disabled && this.node.attributes.href){
17454                 this.fireEvent("click", this.node, e);
17455                 return;
17456             }
17457             e.preventDefault();
17458             if(this.disabled){
17459                 return;
17460             }
17461
17462             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17463                 this.node.toggle();
17464             }
17465
17466             this.fireEvent("click", this.node, e);
17467         }else{
17468             e.stopEvent();
17469         }
17470     },
17471
17472     onDblClick : function(e){
17473         e.preventDefault();
17474         if(this.disabled){
17475             return;
17476         }
17477         if(this.checkbox){
17478             this.toggleCheck();
17479         }
17480         if(!this.animating && this.node.hasChildNodes()){
17481             this.node.toggle();
17482         }
17483         this.fireEvent("dblclick", this.node, e);
17484     },
17485
17486     onCheckChange : function(){
17487         var checked = this.checkbox.checked;
17488         this.node.attributes.checked = checked;
17489         this.fireEvent('checkchange', this.node, checked);
17490     },
17491
17492     ecClick : function(e){
17493         if(!this.animating && this.node.hasChildNodes()){
17494             this.node.toggle();
17495         }
17496     },
17497
17498     startDrop : function(){
17499         this.dropping = true;
17500     },
17501
17502     // delayed drop so the click event doesn't get fired on a drop
17503     endDrop : function(){
17504        setTimeout(function(){
17505            this.dropping = false;
17506        }.createDelegate(this), 50);
17507     },
17508
17509     expand : function(){
17510         this.updateExpandIcon();
17511         this.ctNode.style.display = "";
17512     },
17513
17514     focus : function(){
17515         if(!this.node.preventHScroll){
17516             try{this.anchor.focus();
17517             }catch(e){}
17518         }else if(!Roo.isIE){
17519             try{
17520                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17521                 var l = noscroll.scrollLeft;
17522                 this.anchor.focus();
17523                 noscroll.scrollLeft = l;
17524             }catch(e){}
17525         }
17526     },
17527
17528     toggleCheck : function(value){
17529         var cb = this.checkbox;
17530         if(cb){
17531             cb.checked = (value === undefined ? !cb.checked : value);
17532         }
17533     },
17534
17535     blur : function(){
17536         try{
17537             this.anchor.blur();
17538         }catch(e){}
17539     },
17540
17541     animExpand : function(callback){
17542         var ct = Roo.get(this.ctNode);
17543         ct.stopFx();
17544         if(!this.node.hasChildNodes()){
17545             this.updateExpandIcon();
17546             this.ctNode.style.display = "";
17547             Roo.callback(callback);
17548             return;
17549         }
17550         this.animating = true;
17551         this.updateExpandIcon();
17552
17553         ct.slideIn('t', {
17554            callback : function(){
17555                this.animating = false;
17556                Roo.callback(callback);
17557             },
17558             scope: this,
17559             duration: this.node.ownerTree.duration || .25
17560         });
17561     },
17562
17563     highlight : function(){
17564         var tree = this.node.getOwnerTree();
17565         Roo.fly(this.wrap).highlight(
17566             tree.hlColor || "C3DAF9",
17567             {endColor: tree.hlBaseColor}
17568         );
17569     },
17570
17571     collapse : function(){
17572         this.updateExpandIcon();
17573         this.ctNode.style.display = "none";
17574     },
17575
17576     animCollapse : function(callback){
17577         var ct = Roo.get(this.ctNode);
17578         ct.enableDisplayMode('block');
17579         ct.stopFx();
17580
17581         this.animating = true;
17582         this.updateExpandIcon();
17583
17584         ct.slideOut('t', {
17585             callback : function(){
17586                this.animating = false;
17587                Roo.callback(callback);
17588             },
17589             scope: this,
17590             duration: this.node.ownerTree.duration || .25
17591         });
17592     },
17593
17594     getContainer : function(){
17595         return this.ctNode;
17596     },
17597
17598     getEl : function(){
17599         return this.wrap;
17600     },
17601
17602     appendDDGhost : function(ghostNode){
17603         ghostNode.appendChild(this.elNode.cloneNode(true));
17604     },
17605
17606     getDDRepairXY : function(){
17607         return Roo.lib.Dom.getXY(this.iconNode);
17608     },
17609
17610     onRender : function(){
17611         this.render();
17612     },
17613
17614     render : function(bulkRender){
17615         var n = this.node, a = n.attributes;
17616         var targetNode = n.parentNode ?
17617               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17618
17619         if(!this.rendered){
17620             this.rendered = true;
17621
17622             this.renderElements(n, a, targetNode, bulkRender);
17623
17624             if(a.qtip){
17625                if(this.textNode.setAttributeNS){
17626                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17627                    if(a.qtipTitle){
17628                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17629                    }
17630                }else{
17631                    this.textNode.setAttribute("ext:qtip", a.qtip);
17632                    if(a.qtipTitle){
17633                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17634                    }
17635                }
17636             }else if(a.qtipCfg){
17637                 a.qtipCfg.target = Roo.id(this.textNode);
17638                 Roo.QuickTips.register(a.qtipCfg);
17639             }
17640             this.initEvents();
17641             if(!this.node.expanded){
17642                 this.updateExpandIcon();
17643             }
17644         }else{
17645             if(bulkRender === true) {
17646                 targetNode.appendChild(this.wrap);
17647             }
17648         }
17649     },
17650
17651     renderElements : function(n, a, targetNode, bulkRender)
17652     {
17653         // add some indent caching, this helps performance when rendering a large tree
17654         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17655         var t = n.getOwnerTree();
17656         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17657         if (typeof(n.attributes.html) != 'undefined') {
17658             txt = n.attributes.html;
17659         }
17660         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17661         var cb = typeof a.checked == 'boolean';
17662         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17663         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17664             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17665             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17666             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17667             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17668             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17669              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17670                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17671             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17672             "</li>"];
17673
17674         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17675             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17676                                 n.nextSibling.ui.getEl(), buf.join(""));
17677         }else{
17678             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17679         }
17680
17681         this.elNode = this.wrap.childNodes[0];
17682         this.ctNode = this.wrap.childNodes[1];
17683         var cs = this.elNode.childNodes;
17684         this.indentNode = cs[0];
17685         this.ecNode = cs[1];
17686         this.iconNode = cs[2];
17687         var index = 3;
17688         if(cb){
17689             this.checkbox = cs[3];
17690             index++;
17691         }
17692         this.anchor = cs[index];
17693         this.textNode = cs[index].firstChild;
17694     },
17695
17696     getAnchor : function(){
17697         return this.anchor;
17698     },
17699
17700     getTextEl : function(){
17701         return this.textNode;
17702     },
17703
17704     getIconEl : function(){
17705         return this.iconNode;
17706     },
17707
17708     isChecked : function(){
17709         return this.checkbox ? this.checkbox.checked : false;
17710     },
17711
17712     updateExpandIcon : function(){
17713         if(this.rendered){
17714             var n = this.node, c1, c2;
17715             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17716             var hasChild = n.hasChildNodes();
17717             if(hasChild){
17718                 if(n.expanded){
17719                     cls += "-minus";
17720                     c1 = "x-tree-node-collapsed";
17721                     c2 = "x-tree-node-expanded";
17722                 }else{
17723                     cls += "-plus";
17724                     c1 = "x-tree-node-expanded";
17725                     c2 = "x-tree-node-collapsed";
17726                 }
17727                 if(this.wasLeaf){
17728                     this.removeClass("x-tree-node-leaf");
17729                     this.wasLeaf = false;
17730                 }
17731                 if(this.c1 != c1 || this.c2 != c2){
17732                     Roo.fly(this.elNode).replaceClass(c1, c2);
17733                     this.c1 = c1; this.c2 = c2;
17734                 }
17735             }else{
17736                 // this changes non-leafs into leafs if they have no children.
17737                 // it's not very rational behaviour..
17738                 
17739                 if(!this.wasLeaf && this.node.leaf){
17740                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17741                     delete this.c1;
17742                     delete this.c2;
17743                     this.wasLeaf = true;
17744                 }
17745             }
17746             var ecc = "x-tree-ec-icon "+cls;
17747             if(this.ecc != ecc){
17748                 this.ecNode.className = ecc;
17749                 this.ecc = ecc;
17750             }
17751         }
17752     },
17753
17754     getChildIndent : function(){
17755         if(!this.childIndent){
17756             var buf = [];
17757             var p = this.node;
17758             while(p){
17759                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17760                     if(!p.isLast()) {
17761                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17762                     } else {
17763                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17764                     }
17765                 }
17766                 p = p.parentNode;
17767             }
17768             this.childIndent = buf.join("");
17769         }
17770         return this.childIndent;
17771     },
17772
17773     renderIndent : function(){
17774         if(this.rendered){
17775             var indent = "";
17776             var p = this.node.parentNode;
17777             if(p){
17778                 indent = p.ui.getChildIndent();
17779             }
17780             if(this.indentMarkup != indent){ // don't rerender if not required
17781                 this.indentNode.innerHTML = indent;
17782                 this.indentMarkup = indent;
17783             }
17784             this.updateExpandIcon();
17785         }
17786     }
17787 };
17788
17789 Roo.tree.RootTreeNodeUI = function(){
17790     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17791 };
17792 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17793     render : function(){
17794         if(!this.rendered){
17795             var targetNode = this.node.ownerTree.innerCt.dom;
17796             this.node.expanded = true;
17797             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17798             this.wrap = this.ctNode = targetNode.firstChild;
17799         }
17800     },
17801     collapse : function(){
17802     },
17803     expand : function(){
17804     }
17805 });/*
17806  * Based on:
17807  * Ext JS Library 1.1.1
17808  * Copyright(c) 2006-2007, Ext JS, LLC.
17809  *
17810  * Originally Released Under LGPL - original licence link has changed is not relivant.
17811  *
17812  * Fork - LGPL
17813  * <script type="text/javascript">
17814  */
17815 /**
17816  * @class Roo.tree.TreeLoader
17817  * @extends Roo.util.Observable
17818  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17819  * nodes from a specified URL. The response must be a javascript Array definition
17820  * who's elements are node definition objects. eg:
17821  * <pre><code>
17822 {  success : true,
17823    data :      [
17824    
17825     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17826     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17827     ]
17828 }
17829
17830
17831 </code></pre>
17832  * <br><br>
17833  * The old style respose with just an array is still supported, but not recommended.
17834  * <br><br>
17835  *
17836  * A server request is sent, and child nodes are loaded only when a node is expanded.
17837  * The loading node's id is passed to the server under the parameter name "node" to
17838  * enable the server to produce the correct child nodes.
17839  * <br><br>
17840  * To pass extra parameters, an event handler may be attached to the "beforeload"
17841  * event, and the parameters specified in the TreeLoader's baseParams property:
17842  * <pre><code>
17843     myTreeLoader.on("beforeload", function(treeLoader, node) {
17844         this.baseParams.category = node.attributes.category;
17845     }, this);
17846 </code></pre><
17847  * This would pass an HTTP parameter called "category" to the server containing
17848  * the value of the Node's "category" attribute.
17849  * @constructor
17850  * Creates a new Treeloader.
17851  * @param {Object} config A config object containing config properties.
17852  */
17853 Roo.tree.TreeLoader = function(config){
17854     this.baseParams = {};
17855     this.requestMethod = "POST";
17856     Roo.apply(this, config);
17857
17858     this.addEvents({
17859     
17860         /**
17861          * @event beforeload
17862          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17863          * @param {Object} This TreeLoader object.
17864          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17865          * @param {Object} callback The callback function specified in the {@link #load} call.
17866          */
17867         beforeload : true,
17868         /**
17869          * @event load
17870          * Fires when the node has been successfuly loaded.
17871          * @param {Object} This TreeLoader object.
17872          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17873          * @param {Object} response The response object containing the data from the server.
17874          */
17875         load : true,
17876         /**
17877          * @event loadexception
17878          * Fires if the network request failed.
17879          * @param {Object} This TreeLoader object.
17880          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17881          * @param {Object} response The response object containing the data from the server.
17882          */
17883         loadexception : true,
17884         /**
17885          * @event create
17886          * Fires before a node is created, enabling you to return custom Node types 
17887          * @param {Object} This TreeLoader object.
17888          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17889          */
17890         create : true
17891     });
17892
17893     Roo.tree.TreeLoader.superclass.constructor.call(this);
17894 };
17895
17896 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17897     /**
17898     * @cfg {String} dataUrl The URL from which to request a Json string which
17899     * specifies an array of node definition object representing the child nodes
17900     * to be loaded.
17901     */
17902     /**
17903     * @cfg {String} requestMethod either GET or POST
17904     * defaults to POST (due to BC)
17905     * to be loaded.
17906     */
17907     /**
17908     * @cfg {Object} baseParams (optional) An object containing properties which
17909     * specify HTTP parameters to be passed to each request for child nodes.
17910     */
17911     /**
17912     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17913     * created by this loader. If the attributes sent by the server have an attribute in this object,
17914     * they take priority.
17915     */
17916     /**
17917     * @cfg {Object} uiProviders (optional) An object containing properties which
17918     * 
17919     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17920     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17921     * <i>uiProvider</i> attribute of a returned child node is a string rather
17922     * than a reference to a TreeNodeUI implementation, this that string value
17923     * is used as a property name in the uiProviders object. You can define the provider named
17924     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
17925     */
17926     uiProviders : {},
17927
17928     /**
17929     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
17930     * child nodes before loading.
17931     */
17932     clearOnLoad : true,
17933
17934     /**
17935     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
17936     * property on loading, rather than expecting an array. (eg. more compatible to a standard
17937     * Grid query { data : [ .....] }
17938     */
17939     
17940     root : false,
17941      /**
17942     * @cfg {String} queryParam (optional) 
17943     * Name of the query as it will be passed on the querystring (defaults to 'node')
17944     * eg. the request will be ?node=[id]
17945     */
17946     
17947     
17948     queryParam: false,
17949     
17950     /**
17951      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
17952      * This is called automatically when a node is expanded, but may be used to reload
17953      * a node (or append new children if the {@link #clearOnLoad} option is false.)
17954      * @param {Roo.tree.TreeNode} node
17955      * @param {Function} callback
17956      */
17957     load : function(node, callback){
17958         if(this.clearOnLoad){
17959             while(node.firstChild){
17960                 node.removeChild(node.firstChild);
17961             }
17962         }
17963         if(node.attributes.children){ // preloaded json children
17964             var cs = node.attributes.children;
17965             for(var i = 0, len = cs.length; i < len; i++){
17966                 node.appendChild(this.createNode(cs[i]));
17967             }
17968             if(typeof callback == "function"){
17969                 callback();
17970             }
17971         }else if(this.dataUrl){
17972             this.requestData(node, callback);
17973         }
17974     },
17975
17976     getParams: function(node){
17977         var buf = [], bp = this.baseParams;
17978         for(var key in bp){
17979             if(typeof bp[key] != "function"){
17980                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
17981             }
17982         }
17983         var n = this.queryParam === false ? 'node' : this.queryParam;
17984         buf.push(n + "=", encodeURIComponent(node.id));
17985         return buf.join("");
17986     },
17987
17988     requestData : function(node, callback){
17989         if(this.fireEvent("beforeload", this, node, callback) !== false){
17990             this.transId = Roo.Ajax.request({
17991                 method:this.requestMethod,
17992                 url: this.dataUrl||this.url,
17993                 success: this.handleResponse,
17994                 failure: this.handleFailure,
17995                 scope: this,
17996                 argument: {callback: callback, node: node},
17997                 params: this.getParams(node)
17998             });
17999         }else{
18000             // if the load is cancelled, make sure we notify
18001             // the node that we are done
18002             if(typeof callback == "function"){
18003                 callback();
18004             }
18005         }
18006     },
18007
18008     isLoading : function(){
18009         return this.transId ? true : false;
18010     },
18011
18012     abort : function(){
18013         if(this.isLoading()){
18014             Roo.Ajax.abort(this.transId);
18015         }
18016     },
18017
18018     // private
18019     createNode : function(attr)
18020     {
18021         // apply baseAttrs, nice idea Corey!
18022         if(this.baseAttrs){
18023             Roo.applyIf(attr, this.baseAttrs);
18024         }
18025         if(this.applyLoader !== false){
18026             attr.loader = this;
18027         }
18028         // uiProvider = depreciated..
18029         
18030         if(typeof(attr.uiProvider) == 'string'){
18031            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18032                 /**  eval:var:attr */ eval(attr.uiProvider);
18033         }
18034         if(typeof(this.uiProviders['default']) != 'undefined') {
18035             attr.uiProvider = this.uiProviders['default'];
18036         }
18037         
18038         this.fireEvent('create', this, attr);
18039         
18040         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18041         return(attr.leaf ?
18042                         new Roo.tree.TreeNode(attr) :
18043                         new Roo.tree.AsyncTreeNode(attr));
18044     },
18045
18046     processResponse : function(response, node, callback)
18047     {
18048         var json = response.responseText;
18049         try {
18050             
18051             var o = Roo.decode(json);
18052             
18053             if (this.root === false && typeof(o.success) != undefined) {
18054                 this.root = 'data'; // the default behaviour for list like data..
18055                 }
18056                 
18057             if (this.root !== false &&  !o.success) {
18058                 // it's a failure condition.
18059                 var a = response.argument;
18060                 this.fireEvent("loadexception", this, a.node, response);
18061                 Roo.log("Load failed - should have a handler really");
18062                 return;
18063             }
18064             
18065             
18066             
18067             if (this.root !== false) {
18068                  o = o[this.root];
18069             }
18070             
18071             for(var i = 0, len = o.length; i < len; i++){
18072                 var n = this.createNode(o[i]);
18073                 if(n){
18074                     node.appendChild(n);
18075                 }
18076             }
18077             if(typeof callback == "function"){
18078                 callback(this, node);
18079             }
18080         }catch(e){
18081             this.handleFailure(response);
18082         }
18083     },
18084
18085     handleResponse : function(response){
18086         this.transId = false;
18087         var a = response.argument;
18088         this.processResponse(response, a.node, a.callback);
18089         this.fireEvent("load", this, a.node, response);
18090     },
18091
18092     handleFailure : function(response)
18093     {
18094         // should handle failure better..
18095         this.transId = false;
18096         var a = response.argument;
18097         this.fireEvent("loadexception", this, a.node, response);
18098         if(typeof a.callback == "function"){
18099             a.callback(this, a.node);
18100         }
18101     }
18102 });/*
18103  * Based on:
18104  * Ext JS Library 1.1.1
18105  * Copyright(c) 2006-2007, Ext JS, LLC.
18106  *
18107  * Originally Released Under LGPL - original licence link has changed is not relivant.
18108  *
18109  * Fork - LGPL
18110  * <script type="text/javascript">
18111  */
18112
18113 /**
18114 * @class Roo.tree.TreeFilter
18115 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18116 * @param {TreePanel} tree
18117 * @param {Object} config (optional)
18118  */
18119 Roo.tree.TreeFilter = function(tree, config){
18120     this.tree = tree;
18121     this.filtered = {};
18122     Roo.apply(this, config);
18123 };
18124
18125 Roo.tree.TreeFilter.prototype = {
18126     clearBlank:false,
18127     reverse:false,
18128     autoClear:false,
18129     remove:false,
18130
18131      /**
18132      * Filter the data by a specific attribute.
18133      * @param {String/RegExp} value Either string that the attribute value
18134      * should start with or a RegExp to test against the attribute
18135      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18136      * @param {TreeNode} startNode (optional) The node to start the filter at.
18137      */
18138     filter : function(value, attr, startNode){
18139         attr = attr || "text";
18140         var f;
18141         if(typeof value == "string"){
18142             var vlen = value.length;
18143             // auto clear empty filter
18144             if(vlen == 0 && this.clearBlank){
18145                 this.clear();
18146                 return;
18147             }
18148             value = value.toLowerCase();
18149             f = function(n){
18150                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18151             };
18152         }else if(value.exec){ // regex?
18153             f = function(n){
18154                 return value.test(n.attributes[attr]);
18155             };
18156         }else{
18157             throw 'Illegal filter type, must be string or regex';
18158         }
18159         this.filterBy(f, null, startNode);
18160         },
18161
18162     /**
18163      * Filter by a function. The passed function will be called with each
18164      * node in the tree (or from the startNode). If the function returns true, the node is kept
18165      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18166      * @param {Function} fn The filter function
18167      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18168      */
18169     filterBy : function(fn, scope, startNode){
18170         startNode = startNode || this.tree.root;
18171         if(this.autoClear){
18172             this.clear();
18173         }
18174         var af = this.filtered, rv = this.reverse;
18175         var f = function(n){
18176             if(n == startNode){
18177                 return true;
18178             }
18179             if(af[n.id]){
18180                 return false;
18181             }
18182             var m = fn.call(scope || n, n);
18183             if(!m || rv){
18184                 af[n.id] = n;
18185                 n.ui.hide();
18186                 return false;
18187             }
18188             return true;
18189         };
18190         startNode.cascade(f);
18191         if(this.remove){
18192            for(var id in af){
18193                if(typeof id != "function"){
18194                    var n = af[id];
18195                    if(n && n.parentNode){
18196                        n.parentNode.removeChild(n);
18197                    }
18198                }
18199            }
18200         }
18201     },
18202
18203     /**
18204      * Clears the current filter. Note: with the "remove" option
18205      * set a filter cannot be cleared.
18206      */
18207     clear : function(){
18208         var t = this.tree;
18209         var af = this.filtered;
18210         for(var id in af){
18211             if(typeof id != "function"){
18212                 var n = af[id];
18213                 if(n){
18214                     n.ui.show();
18215                 }
18216             }
18217         }
18218         this.filtered = {};
18219     }
18220 };
18221 /*
18222  * Based on:
18223  * Ext JS Library 1.1.1
18224  * Copyright(c) 2006-2007, Ext JS, LLC.
18225  *
18226  * Originally Released Under LGPL - original licence link has changed is not relivant.
18227  *
18228  * Fork - LGPL
18229  * <script type="text/javascript">
18230  */
18231  
18232
18233 /**
18234  * @class Roo.tree.TreeSorter
18235  * Provides sorting of nodes in a TreePanel
18236  * 
18237  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18238  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18239  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18240  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18241  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18242  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18243  * @constructor
18244  * @param {TreePanel} tree
18245  * @param {Object} config
18246  */
18247 Roo.tree.TreeSorter = function(tree, config){
18248     Roo.apply(this, config);
18249     tree.on("beforechildrenrendered", this.doSort, this);
18250     tree.on("append", this.updateSort, this);
18251     tree.on("insert", this.updateSort, this);
18252     
18253     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18254     var p = this.property || "text";
18255     var sortType = this.sortType;
18256     var fs = this.folderSort;
18257     var cs = this.caseSensitive === true;
18258     var leafAttr = this.leafAttr || 'leaf';
18259
18260     this.sortFn = function(n1, n2){
18261         if(fs){
18262             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18263                 return 1;
18264             }
18265             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18266                 return -1;
18267             }
18268         }
18269         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18270         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18271         if(v1 < v2){
18272                         return dsc ? +1 : -1;
18273                 }else if(v1 > v2){
18274                         return dsc ? -1 : +1;
18275         }else{
18276                 return 0;
18277         }
18278     };
18279 };
18280
18281 Roo.tree.TreeSorter.prototype = {
18282     doSort : function(node){
18283         node.sort(this.sortFn);
18284     },
18285     
18286     compareNodes : function(n1, n2){
18287         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18288     },
18289     
18290     updateSort : function(tree, node){
18291         if(node.childrenRendered){
18292             this.doSort.defer(1, this, [node]);
18293         }
18294     }
18295 };/*
18296  * Based on:
18297  * Ext JS Library 1.1.1
18298  * Copyright(c) 2006-2007, Ext JS, LLC.
18299  *
18300  * Originally Released Under LGPL - original licence link has changed is not relivant.
18301  *
18302  * Fork - LGPL
18303  * <script type="text/javascript">
18304  */
18305
18306 if(Roo.dd.DropZone){
18307     
18308 Roo.tree.TreeDropZone = function(tree, config){
18309     this.allowParentInsert = false;
18310     this.allowContainerDrop = false;
18311     this.appendOnly = false;
18312     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18313     this.tree = tree;
18314     this.lastInsertClass = "x-tree-no-status";
18315     this.dragOverData = {};
18316 };
18317
18318 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18319     ddGroup : "TreeDD",
18320     scroll:  true,
18321     
18322     expandDelay : 1000,
18323     
18324     expandNode : function(node){
18325         if(node.hasChildNodes() && !node.isExpanded()){
18326             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18327         }
18328     },
18329     
18330     queueExpand : function(node){
18331         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18332     },
18333     
18334     cancelExpand : function(){
18335         if(this.expandProcId){
18336             clearTimeout(this.expandProcId);
18337             this.expandProcId = false;
18338         }
18339     },
18340     
18341     isValidDropPoint : function(n, pt, dd, e, data){
18342         if(!n || !data){ return false; }
18343         var targetNode = n.node;
18344         var dropNode = data.node;
18345         // default drop rules
18346         if(!(targetNode && targetNode.isTarget && pt)){
18347             return false;
18348         }
18349         if(pt == "append" && targetNode.allowChildren === false){
18350             return false;
18351         }
18352         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18353             return false;
18354         }
18355         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18356             return false;
18357         }
18358         // reuse the object
18359         var overEvent = this.dragOverData;
18360         overEvent.tree = this.tree;
18361         overEvent.target = targetNode;
18362         overEvent.data = data;
18363         overEvent.point = pt;
18364         overEvent.source = dd;
18365         overEvent.rawEvent = e;
18366         overEvent.dropNode = dropNode;
18367         overEvent.cancel = false;  
18368         var result = this.tree.fireEvent("nodedragover", overEvent);
18369         return overEvent.cancel === false && result !== false;
18370     },
18371     
18372     getDropPoint : function(e, n, dd)
18373     {
18374         var tn = n.node;
18375         if(tn.isRoot){
18376             return tn.allowChildren !== false ? "append" : false; // always append for root
18377         }
18378         var dragEl = n.ddel;
18379         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18380         var y = Roo.lib.Event.getPageY(e);
18381         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18382         
18383         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18384         var noAppend = tn.allowChildren === false;
18385         if(this.appendOnly || tn.parentNode.allowChildren === false){
18386             return noAppend ? false : "append";
18387         }
18388         var noBelow = false;
18389         if(!this.allowParentInsert){
18390             noBelow = tn.hasChildNodes() && tn.isExpanded();
18391         }
18392         var q = (b - t) / (noAppend ? 2 : 3);
18393         if(y >= t && y < (t + q)){
18394             return "above";
18395         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18396             return "below";
18397         }else{
18398             return "append";
18399         }
18400     },
18401     
18402     onNodeEnter : function(n, dd, e, data)
18403     {
18404         this.cancelExpand();
18405     },
18406     
18407     onNodeOver : function(n, dd, e, data)
18408     {
18409        
18410         var pt = this.getDropPoint(e, n, dd);
18411         var node = n.node;
18412         
18413         // auto node expand check
18414         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18415             this.queueExpand(node);
18416         }else if(pt != "append"){
18417             this.cancelExpand();
18418         }
18419         
18420         // set the insert point style on the target node
18421         var returnCls = this.dropNotAllowed;
18422         if(this.isValidDropPoint(n, pt, dd, e, data)){
18423            if(pt){
18424                var el = n.ddel;
18425                var cls;
18426                if(pt == "above"){
18427                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18428                    cls = "x-tree-drag-insert-above";
18429                }else if(pt == "below"){
18430                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18431                    cls = "x-tree-drag-insert-below";
18432                }else{
18433                    returnCls = "x-tree-drop-ok-append";
18434                    cls = "x-tree-drag-append";
18435                }
18436                if(this.lastInsertClass != cls){
18437                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18438                    this.lastInsertClass = cls;
18439                }
18440            }
18441        }
18442        return returnCls;
18443     },
18444     
18445     onNodeOut : function(n, dd, e, data){
18446         
18447         this.cancelExpand();
18448         this.removeDropIndicators(n);
18449     },
18450     
18451     onNodeDrop : function(n, dd, e, data){
18452         var point = this.getDropPoint(e, n, dd);
18453         var targetNode = n.node;
18454         targetNode.ui.startDrop();
18455         if(!this.isValidDropPoint(n, point, dd, e, data)){
18456             targetNode.ui.endDrop();
18457             return false;
18458         }
18459         // first try to find the drop node
18460         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18461         var dropEvent = {
18462             tree : this.tree,
18463             target: targetNode,
18464             data: data,
18465             point: point,
18466             source: dd,
18467             rawEvent: e,
18468             dropNode: dropNode,
18469             cancel: !dropNode   
18470         };
18471         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18472         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18473             targetNode.ui.endDrop();
18474             return false;
18475         }
18476         // allow target changing
18477         targetNode = dropEvent.target;
18478         if(point == "append" && !targetNode.isExpanded()){
18479             targetNode.expand(false, null, function(){
18480                 this.completeDrop(dropEvent);
18481             }.createDelegate(this));
18482         }else{
18483             this.completeDrop(dropEvent);
18484         }
18485         return true;
18486     },
18487     
18488     completeDrop : function(de){
18489         var ns = de.dropNode, p = de.point, t = de.target;
18490         if(!(ns instanceof Array)){
18491             ns = [ns];
18492         }
18493         var n;
18494         for(var i = 0, len = ns.length; i < len; i++){
18495             n = ns[i];
18496             if(p == "above"){
18497                 t.parentNode.insertBefore(n, t);
18498             }else if(p == "below"){
18499                 t.parentNode.insertBefore(n, t.nextSibling);
18500             }else{
18501                 t.appendChild(n);
18502             }
18503         }
18504         n.ui.focus();
18505         if(this.tree.hlDrop){
18506             n.ui.highlight();
18507         }
18508         t.ui.endDrop();
18509         this.tree.fireEvent("nodedrop", de);
18510     },
18511     
18512     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18513         if(this.tree.hlDrop){
18514             dropNode.ui.focus();
18515             dropNode.ui.highlight();
18516         }
18517         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18518     },
18519     
18520     getTree : function(){
18521         return this.tree;
18522     },
18523     
18524     removeDropIndicators : function(n){
18525         if(n && n.ddel){
18526             var el = n.ddel;
18527             Roo.fly(el).removeClass([
18528                     "x-tree-drag-insert-above",
18529                     "x-tree-drag-insert-below",
18530                     "x-tree-drag-append"]);
18531             this.lastInsertClass = "_noclass";
18532         }
18533     },
18534     
18535     beforeDragDrop : function(target, e, id){
18536         this.cancelExpand();
18537         return true;
18538     },
18539     
18540     afterRepair : function(data){
18541         if(data && Roo.enableFx){
18542             data.node.ui.highlight();
18543         }
18544         this.hideProxy();
18545     } 
18546     
18547 });
18548
18549 }
18550 /*
18551  * Based on:
18552  * Ext JS Library 1.1.1
18553  * Copyright(c) 2006-2007, Ext JS, LLC.
18554  *
18555  * Originally Released Under LGPL - original licence link has changed is not relivant.
18556  *
18557  * Fork - LGPL
18558  * <script type="text/javascript">
18559  */
18560  
18561
18562 if(Roo.dd.DragZone){
18563 Roo.tree.TreeDragZone = function(tree, config){
18564     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18565     this.tree = tree;
18566 };
18567
18568 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18569     ddGroup : "TreeDD",
18570    
18571     onBeforeDrag : function(data, e){
18572         var n = data.node;
18573         return n && n.draggable && !n.disabled;
18574     },
18575      
18576     
18577     onInitDrag : function(e){
18578         var data = this.dragData;
18579         this.tree.getSelectionModel().select(data.node);
18580         this.proxy.update("");
18581         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18582         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18583     },
18584     
18585     getRepairXY : function(e, data){
18586         return data.node.ui.getDDRepairXY();
18587     },
18588     
18589     onEndDrag : function(data, e){
18590         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18591         
18592         
18593     },
18594     
18595     onValidDrop : function(dd, e, id){
18596         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18597         this.hideProxy();
18598     },
18599     
18600     beforeInvalidDrop : function(e, id){
18601         // this scrolls the original position back into view
18602         var sm = this.tree.getSelectionModel();
18603         sm.clearSelections();
18604         sm.select(this.dragData.node);
18605     }
18606 });
18607 }/*
18608  * Based on:
18609  * Ext JS Library 1.1.1
18610  * Copyright(c) 2006-2007, Ext JS, LLC.
18611  *
18612  * Originally Released Under LGPL - original licence link has changed is not relivant.
18613  *
18614  * Fork - LGPL
18615  * <script type="text/javascript">
18616  */
18617 /**
18618  * @class Roo.tree.TreeEditor
18619  * @extends Roo.Editor
18620  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18621  * as the editor field.
18622  * @constructor
18623  * @param {Object} config (used to be the tree panel.)
18624  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18625  * 
18626  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18627  * @cfg {Roo.form.TextField|Object} field The field configuration
18628  *
18629  * 
18630  */
18631 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18632     var tree = config;
18633     var field;
18634     if (oldconfig) { // old style..
18635         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18636     } else {
18637         // new style..
18638         tree = config.tree;
18639         config.field = config.field  || {};
18640         config.field.xtype = 'TextField';
18641         field = Roo.factory(config.field, Roo.form);
18642     }
18643     config = config || {};
18644     
18645     
18646     this.addEvents({
18647         /**
18648          * @event beforenodeedit
18649          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18650          * false from the handler of this event.
18651          * @param {Editor} this
18652          * @param {Roo.tree.Node} node 
18653          */
18654         "beforenodeedit" : true
18655     });
18656     
18657     //Roo.log(config);
18658     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18659
18660     this.tree = tree;
18661
18662     tree.on('beforeclick', this.beforeNodeClick, this);
18663     tree.getTreeEl().on('mousedown', this.hide, this);
18664     this.on('complete', this.updateNode, this);
18665     this.on('beforestartedit', this.fitToTree, this);
18666     this.on('startedit', this.bindScroll, this, {delay:10});
18667     this.on('specialkey', this.onSpecialKey, this);
18668 };
18669
18670 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18671     /**
18672      * @cfg {String} alignment
18673      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18674      */
18675     alignment: "l-l",
18676     // inherit
18677     autoSize: false,
18678     /**
18679      * @cfg {Boolean} hideEl
18680      * True to hide the bound element while the editor is displayed (defaults to false)
18681      */
18682     hideEl : false,
18683     /**
18684      * @cfg {String} cls
18685      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18686      */
18687     cls: "x-small-editor x-tree-editor",
18688     /**
18689      * @cfg {Boolean} shim
18690      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18691      */
18692     shim:false,
18693     // inherit
18694     shadow:"frame",
18695     /**
18696      * @cfg {Number} maxWidth
18697      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18698      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18699      * scroll and client offsets into account prior to each edit.
18700      */
18701     maxWidth: 250,
18702
18703     editDelay : 350,
18704
18705     // private
18706     fitToTree : function(ed, el){
18707         var td = this.tree.getTreeEl().dom, nd = el.dom;
18708         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18709             td.scrollLeft = nd.offsetLeft;
18710         }
18711         var w = Math.min(
18712                 this.maxWidth,
18713                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18714         this.setSize(w, '');
18715         
18716         return this.fireEvent('beforenodeedit', this, this.editNode);
18717         
18718     },
18719
18720     // private
18721     triggerEdit : function(node){
18722         this.completeEdit();
18723         this.editNode = node;
18724         this.startEdit(node.ui.textNode, node.text);
18725     },
18726
18727     // private
18728     bindScroll : function(){
18729         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18730     },
18731
18732     // private
18733     beforeNodeClick : function(node, e){
18734         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18735         this.lastClick = new Date();
18736         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18737             e.stopEvent();
18738             this.triggerEdit(node);
18739             return false;
18740         }
18741         return true;
18742     },
18743
18744     // private
18745     updateNode : function(ed, value){
18746         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18747         this.editNode.setText(value);
18748     },
18749
18750     // private
18751     onHide : function(){
18752         Roo.tree.TreeEditor.superclass.onHide.call(this);
18753         if(this.editNode){
18754             this.editNode.ui.focus();
18755         }
18756     },
18757
18758     // private
18759     onSpecialKey : function(field, e){
18760         var k = e.getKey();
18761         if(k == e.ESC){
18762             e.stopEvent();
18763             this.cancelEdit();
18764         }else if(k == e.ENTER && !e.hasModifier()){
18765             e.stopEvent();
18766             this.completeEdit();
18767         }
18768     }
18769 });//<Script type="text/javascript">
18770 /*
18771  * Based on:
18772  * Ext JS Library 1.1.1
18773  * Copyright(c) 2006-2007, Ext JS, LLC.
18774  *
18775  * Originally Released Under LGPL - original licence link has changed is not relivant.
18776  *
18777  * Fork - LGPL
18778  * <script type="text/javascript">
18779  */
18780  
18781 /**
18782  * Not documented??? - probably should be...
18783  */
18784
18785 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18786     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18787     
18788     renderElements : function(n, a, targetNode, bulkRender){
18789         //consel.log("renderElements?");
18790         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18791
18792         var t = n.getOwnerTree();
18793         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18794         
18795         var cols = t.columns;
18796         var bw = t.borderWidth;
18797         var c = cols[0];
18798         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18799          var cb = typeof a.checked == "boolean";
18800         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18801         var colcls = 'x-t-' + tid + '-c0';
18802         var buf = [
18803             '<li class="x-tree-node">',
18804             
18805                 
18806                 '<div class="x-tree-node-el ', a.cls,'">',
18807                     // extran...
18808                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18809                 
18810                 
18811                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18812                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18813                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18814                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18815                            (a.iconCls ? ' '+a.iconCls : ''),
18816                            '" unselectable="on" />',
18817                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18818                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18819                              
18820                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18821                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18822                             '<span unselectable="on" qtip="' + tx + '">',
18823                              tx,
18824                              '</span></a>' ,
18825                     '</div>',
18826                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18827                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18828                  ];
18829         for(var i = 1, len = cols.length; i < len; i++){
18830             c = cols[i];
18831             colcls = 'x-t-' + tid + '-c' +i;
18832             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18833             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18834                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18835                       "</div>");
18836          }
18837          
18838          buf.push(
18839             '</a>',
18840             '<div class="x-clear"></div></div>',
18841             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18842             "</li>");
18843         
18844         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18845             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18846                                 n.nextSibling.ui.getEl(), buf.join(""));
18847         }else{
18848             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18849         }
18850         var el = this.wrap.firstChild;
18851         this.elRow = el;
18852         this.elNode = el.firstChild;
18853         this.ranchor = el.childNodes[1];
18854         this.ctNode = this.wrap.childNodes[1];
18855         var cs = el.firstChild.childNodes;
18856         this.indentNode = cs[0];
18857         this.ecNode = cs[1];
18858         this.iconNode = cs[2];
18859         var index = 3;
18860         if(cb){
18861             this.checkbox = cs[3];
18862             index++;
18863         }
18864         this.anchor = cs[index];
18865         
18866         this.textNode = cs[index].firstChild;
18867         
18868         //el.on("click", this.onClick, this);
18869         //el.on("dblclick", this.onDblClick, this);
18870         
18871         
18872        // console.log(this);
18873     },
18874     initEvents : function(){
18875         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18876         
18877             
18878         var a = this.ranchor;
18879
18880         var el = Roo.get(a);
18881
18882         if(Roo.isOpera){ // opera render bug ignores the CSS
18883             el.setStyle("text-decoration", "none");
18884         }
18885
18886         el.on("click", this.onClick, this);
18887         el.on("dblclick", this.onDblClick, this);
18888         el.on("contextmenu", this.onContextMenu, this);
18889         
18890     },
18891     
18892     /*onSelectedChange : function(state){
18893         if(state){
18894             this.focus();
18895             this.addClass("x-tree-selected");
18896         }else{
18897             //this.blur();
18898             this.removeClass("x-tree-selected");
18899         }
18900     },*/
18901     addClass : function(cls){
18902         if(this.elRow){
18903             Roo.fly(this.elRow).addClass(cls);
18904         }
18905         
18906     },
18907     
18908     
18909     removeClass : function(cls){
18910         if(this.elRow){
18911             Roo.fly(this.elRow).removeClass(cls);
18912         }
18913     }
18914
18915     
18916     
18917 });//<Script type="text/javascript">
18918
18919 /*
18920  * Based on:
18921  * Ext JS Library 1.1.1
18922  * Copyright(c) 2006-2007, Ext JS, LLC.
18923  *
18924  * Originally Released Under LGPL - original licence link has changed is not relivant.
18925  *
18926  * Fork - LGPL
18927  * <script type="text/javascript">
18928  */
18929  
18930
18931 /**
18932  * @class Roo.tree.ColumnTree
18933  * @extends Roo.data.TreePanel
18934  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
18935  * @cfg {int} borderWidth  compined right/left border allowance
18936  * @constructor
18937  * @param {String/HTMLElement/Element} el The container element
18938  * @param {Object} config
18939  */
18940 Roo.tree.ColumnTree =  function(el, config)
18941 {
18942    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
18943    this.addEvents({
18944         /**
18945         * @event resize
18946         * Fire this event on a container when it resizes
18947         * @param {int} w Width
18948         * @param {int} h Height
18949         */
18950        "resize" : true
18951     });
18952     this.on('resize', this.onResize, this);
18953 };
18954
18955 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
18956     //lines:false,
18957     
18958     
18959     borderWidth: Roo.isBorderBox ? 0 : 2, 
18960     headEls : false,
18961     
18962     render : function(){
18963         // add the header.....
18964        
18965         Roo.tree.ColumnTree.superclass.render.apply(this);
18966         
18967         this.el.addClass('x-column-tree');
18968         
18969         this.headers = this.el.createChild(
18970             {cls:'x-tree-headers'},this.innerCt.dom);
18971    
18972         var cols = this.columns, c;
18973         var totalWidth = 0;
18974         this.headEls = [];
18975         var  len = cols.length;
18976         for(var i = 0; i < len; i++){
18977              c = cols[i];
18978              totalWidth += c.width;
18979             this.headEls.push(this.headers.createChild({
18980                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
18981                  cn: {
18982                      cls:'x-tree-hd-text',
18983                      html: c.header
18984                  },
18985                  style:'width:'+(c.width-this.borderWidth)+'px;'
18986              }));
18987         }
18988         this.headers.createChild({cls:'x-clear'});
18989         // prevent floats from wrapping when clipped
18990         this.headers.setWidth(totalWidth);
18991         //this.innerCt.setWidth(totalWidth);
18992         this.innerCt.setStyle({ overflow: 'auto' });
18993         this.onResize(this.width, this.height);
18994              
18995         
18996     },
18997     onResize : function(w,h)
18998     {
18999         this.height = h;
19000         this.width = w;
19001         // resize cols..
19002         this.innerCt.setWidth(this.width);
19003         this.innerCt.setHeight(this.height-20);
19004         
19005         // headers...
19006         var cols = this.columns, c;
19007         var totalWidth = 0;
19008         var expEl = false;
19009         var len = cols.length;
19010         for(var i = 0; i < len; i++){
19011             c = cols[i];
19012             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19013                 // it's the expander..
19014                 expEl  = this.headEls[i];
19015                 continue;
19016             }
19017             totalWidth += c.width;
19018             
19019         }
19020         if (expEl) {
19021             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19022         }
19023         this.headers.setWidth(w-20);
19024
19025         
19026         
19027         
19028     }
19029 });
19030 /*
19031  * Based on:
19032  * Ext JS Library 1.1.1
19033  * Copyright(c) 2006-2007, Ext JS, LLC.
19034  *
19035  * Originally Released Under LGPL - original licence link has changed is not relivant.
19036  *
19037  * Fork - LGPL
19038  * <script type="text/javascript">
19039  */
19040  
19041 /**
19042  * @class Roo.menu.Menu
19043  * @extends Roo.util.Observable
19044  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19045  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19046  * @constructor
19047  * Creates a new Menu
19048  * @param {Object} config Configuration options
19049  */
19050 Roo.menu.Menu = function(config){
19051     Roo.apply(this, config);
19052     this.id = this.id || Roo.id();
19053     this.addEvents({
19054         /**
19055          * @event beforeshow
19056          * Fires before this menu is displayed
19057          * @param {Roo.menu.Menu} this
19058          */
19059         beforeshow : true,
19060         /**
19061          * @event beforehide
19062          * Fires before this menu is hidden
19063          * @param {Roo.menu.Menu} this
19064          */
19065         beforehide : true,
19066         /**
19067          * @event show
19068          * Fires after this menu is displayed
19069          * @param {Roo.menu.Menu} this
19070          */
19071         show : true,
19072         /**
19073          * @event hide
19074          * Fires after this menu is hidden
19075          * @param {Roo.menu.Menu} this
19076          */
19077         hide : true,
19078         /**
19079          * @event click
19080          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19081          * @param {Roo.menu.Menu} this
19082          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19083          * @param {Roo.EventObject} e
19084          */
19085         click : true,
19086         /**
19087          * @event mouseover
19088          * Fires when the mouse is hovering over this menu
19089          * @param {Roo.menu.Menu} this
19090          * @param {Roo.EventObject} e
19091          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19092          */
19093         mouseover : true,
19094         /**
19095          * @event mouseout
19096          * Fires when the mouse exits this menu
19097          * @param {Roo.menu.Menu} this
19098          * @param {Roo.EventObject} e
19099          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19100          */
19101         mouseout : true,
19102         /**
19103          * @event itemclick
19104          * Fires when a menu item contained in this menu is clicked
19105          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19106          * @param {Roo.EventObject} e
19107          */
19108         itemclick: true
19109     });
19110     if (this.registerMenu) {
19111         Roo.menu.MenuMgr.register(this);
19112     }
19113     
19114     var mis = this.items;
19115     this.items = new Roo.util.MixedCollection();
19116     if(mis){
19117         this.add.apply(this, mis);
19118     }
19119 };
19120
19121 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19122     /**
19123      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19124      */
19125     minWidth : 120,
19126     /**
19127      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19128      * for bottom-right shadow (defaults to "sides")
19129      */
19130     shadow : "sides",
19131     /**
19132      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19133      * this menu (defaults to "tl-tr?")
19134      */
19135     subMenuAlign : "tl-tr?",
19136     /**
19137      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19138      * relative to its element of origin (defaults to "tl-bl?")
19139      */
19140     defaultAlign : "tl-bl?",
19141     /**
19142      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19143      */
19144     allowOtherMenus : false,
19145     /**
19146      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19147      */
19148     registerMenu : true,
19149
19150     hidden:true,
19151
19152     // private
19153     render : function(){
19154         if(this.el){
19155             return;
19156         }
19157         var el = this.el = new Roo.Layer({
19158             cls: "x-menu",
19159             shadow:this.shadow,
19160             constrain: false,
19161             parentEl: this.parentEl || document.body,
19162             zindex:15000
19163         });
19164
19165         this.keyNav = new Roo.menu.MenuNav(this);
19166
19167         if(this.plain){
19168             el.addClass("x-menu-plain");
19169         }
19170         if(this.cls){
19171             el.addClass(this.cls);
19172         }
19173         // generic focus element
19174         this.focusEl = el.createChild({
19175             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19176         });
19177         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19178         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19179         
19180         ul.on("mouseover", this.onMouseOver, this);
19181         ul.on("mouseout", this.onMouseOut, this);
19182         this.items.each(function(item){
19183             if (item.hidden) {
19184                 return;
19185             }
19186             
19187             var li = document.createElement("li");
19188             li.className = "x-menu-list-item";
19189             ul.dom.appendChild(li);
19190             item.render(li, this);
19191         }, this);
19192         this.ul = ul;
19193         this.autoWidth();
19194     },
19195
19196     // private
19197     autoWidth : function(){
19198         var el = this.el, ul = this.ul;
19199         if(!el){
19200             return;
19201         }
19202         var w = this.width;
19203         if(w){
19204             el.setWidth(w);
19205         }else if(Roo.isIE){
19206             el.setWidth(this.minWidth);
19207             var t = el.dom.offsetWidth; // force recalc
19208             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19209         }
19210     },
19211
19212     // private
19213     delayAutoWidth : function(){
19214         if(this.rendered){
19215             if(!this.awTask){
19216                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19217             }
19218             this.awTask.delay(20);
19219         }
19220     },
19221
19222     // private
19223     findTargetItem : function(e){
19224         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19225         if(t && t.menuItemId){
19226             return this.items.get(t.menuItemId);
19227         }
19228     },
19229
19230     // private
19231     onClick : function(e){
19232         Roo.log("menu.onClick");
19233         var t = this.findTargetItem(e);
19234         if(!t){
19235             return;
19236         }
19237         Roo.log(e);
19238         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19239             if(t == this.activeItem && t.shouldDeactivate(e)){
19240                 this.activeItem.deactivate();
19241                 delete this.activeItem;
19242                 return;
19243             }
19244             if(t.canActivate){
19245                 this.setActiveItem(t, true);
19246             }
19247             return;
19248             
19249             
19250         }
19251         
19252         t.onClick(e);
19253         this.fireEvent("click", this, t, e);
19254     },
19255
19256     // private
19257     setActiveItem : function(item, autoExpand){
19258         if(item != this.activeItem){
19259             if(this.activeItem){
19260                 this.activeItem.deactivate();
19261             }
19262             this.activeItem = item;
19263             item.activate(autoExpand);
19264         }else if(autoExpand){
19265             item.expandMenu();
19266         }
19267     },
19268
19269     // private
19270     tryActivate : function(start, step){
19271         var items = this.items;
19272         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19273             var item = items.get(i);
19274             if(!item.disabled && item.canActivate){
19275                 this.setActiveItem(item, false);
19276                 return item;
19277             }
19278         }
19279         return false;
19280     },
19281
19282     // private
19283     onMouseOver : function(e){
19284         var t;
19285         if(t = this.findTargetItem(e)){
19286             if(t.canActivate && !t.disabled){
19287                 this.setActiveItem(t, true);
19288             }
19289         }
19290         this.fireEvent("mouseover", this, e, t);
19291     },
19292
19293     // private
19294     onMouseOut : function(e){
19295         var t;
19296         if(t = this.findTargetItem(e)){
19297             if(t == this.activeItem && t.shouldDeactivate(e)){
19298                 this.activeItem.deactivate();
19299                 delete this.activeItem;
19300             }
19301         }
19302         this.fireEvent("mouseout", this, e, t);
19303     },
19304
19305     /**
19306      * Read-only.  Returns true if the menu is currently displayed, else false.
19307      * @type Boolean
19308      */
19309     isVisible : function(){
19310         return this.el && !this.hidden;
19311     },
19312
19313     /**
19314      * Displays this menu relative to another element
19315      * @param {String/HTMLElement/Roo.Element} element The element to align to
19316      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19317      * the element (defaults to this.defaultAlign)
19318      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19319      */
19320     show : function(el, pos, parentMenu){
19321         this.parentMenu = parentMenu;
19322         if(!this.el){
19323             this.render();
19324         }
19325         this.fireEvent("beforeshow", this);
19326         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19327     },
19328
19329     /**
19330      * Displays this menu at a specific xy position
19331      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19332      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19333      */
19334     showAt : function(xy, parentMenu, /* private: */_e){
19335         this.parentMenu = parentMenu;
19336         if(!this.el){
19337             this.render();
19338         }
19339         if(_e !== false){
19340             this.fireEvent("beforeshow", this);
19341             xy = this.el.adjustForConstraints(xy);
19342         }
19343         this.el.setXY(xy);
19344         this.el.show();
19345         this.hidden = false;
19346         this.focus();
19347         this.fireEvent("show", this);
19348     },
19349
19350     focus : function(){
19351         if(!this.hidden){
19352             this.doFocus.defer(50, this);
19353         }
19354     },
19355
19356     doFocus : function(){
19357         if(!this.hidden){
19358             this.focusEl.focus();
19359         }
19360     },
19361
19362     /**
19363      * Hides this menu and optionally all parent menus
19364      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19365      */
19366     hide : function(deep){
19367         if(this.el && this.isVisible()){
19368             this.fireEvent("beforehide", this);
19369             if(this.activeItem){
19370                 this.activeItem.deactivate();
19371                 this.activeItem = null;
19372             }
19373             this.el.hide();
19374             this.hidden = true;
19375             this.fireEvent("hide", this);
19376         }
19377         if(deep === true && this.parentMenu){
19378             this.parentMenu.hide(true);
19379         }
19380     },
19381
19382     /**
19383      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19384      * Any of the following are valid:
19385      * <ul>
19386      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19387      * <li>An HTMLElement object which will be converted to a menu item</li>
19388      * <li>A menu item config object that will be created as a new menu item</li>
19389      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19390      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19391      * </ul>
19392      * Usage:
19393      * <pre><code>
19394 // Create the menu
19395 var menu = new Roo.menu.Menu();
19396
19397 // Create a menu item to add by reference
19398 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19399
19400 // Add a bunch of items at once using different methods.
19401 // Only the last item added will be returned.
19402 var item = menu.add(
19403     menuItem,                // add existing item by ref
19404     'Dynamic Item',          // new TextItem
19405     '-',                     // new separator
19406     { text: 'Config Item' }  // new item by config
19407 );
19408 </code></pre>
19409      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19410      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19411      */
19412     add : function(){
19413         var a = arguments, l = a.length, item;
19414         for(var i = 0; i < l; i++){
19415             var el = a[i];
19416             if ((typeof(el) == "object") && el.xtype && el.xns) {
19417                 el = Roo.factory(el, Roo.menu);
19418             }
19419             
19420             if(el.render){ // some kind of Item
19421                 item = this.addItem(el);
19422             }else if(typeof el == "string"){ // string
19423                 if(el == "separator" || el == "-"){
19424                     item = this.addSeparator();
19425                 }else{
19426                     item = this.addText(el);
19427                 }
19428             }else if(el.tagName || el.el){ // element
19429                 item = this.addElement(el);
19430             }else if(typeof el == "object"){ // must be menu item config?
19431                 item = this.addMenuItem(el);
19432             }
19433         }
19434         return item;
19435     },
19436
19437     /**
19438      * Returns this menu's underlying {@link Roo.Element} object
19439      * @return {Roo.Element} The element
19440      */
19441     getEl : function(){
19442         if(!this.el){
19443             this.render();
19444         }
19445         return this.el;
19446     },
19447
19448     /**
19449      * Adds a separator bar to the menu
19450      * @return {Roo.menu.Item} The menu item that was added
19451      */
19452     addSeparator : function(){
19453         return this.addItem(new Roo.menu.Separator());
19454     },
19455
19456     /**
19457      * Adds an {@link Roo.Element} object to the menu
19458      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19459      * @return {Roo.menu.Item} The menu item that was added
19460      */
19461     addElement : function(el){
19462         return this.addItem(new Roo.menu.BaseItem(el));
19463     },
19464
19465     /**
19466      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19467      * @param {Roo.menu.Item} item The menu item to add
19468      * @return {Roo.menu.Item} The menu item that was added
19469      */
19470     addItem : function(item){
19471         this.items.add(item);
19472         if(this.ul){
19473             var li = document.createElement("li");
19474             li.className = "x-menu-list-item";
19475             this.ul.dom.appendChild(li);
19476             item.render(li, this);
19477             this.delayAutoWidth();
19478         }
19479         return item;
19480     },
19481
19482     /**
19483      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19484      * @param {Object} config A MenuItem config object
19485      * @return {Roo.menu.Item} The menu item that was added
19486      */
19487     addMenuItem : function(config){
19488         if(!(config instanceof Roo.menu.Item)){
19489             if(typeof config.checked == "boolean"){ // must be check menu item config?
19490                 config = new Roo.menu.CheckItem(config);
19491             }else{
19492                 config = new Roo.menu.Item(config);
19493             }
19494         }
19495         return this.addItem(config);
19496     },
19497
19498     /**
19499      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19500      * @param {String} text The text to display in the menu item
19501      * @return {Roo.menu.Item} The menu item that was added
19502      */
19503     addText : function(text){
19504         return this.addItem(new Roo.menu.TextItem({ text : text }));
19505     },
19506
19507     /**
19508      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19509      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19510      * @param {Roo.menu.Item} item The menu item to add
19511      * @return {Roo.menu.Item} The menu item that was added
19512      */
19513     insert : function(index, item){
19514         this.items.insert(index, item);
19515         if(this.ul){
19516             var li = document.createElement("li");
19517             li.className = "x-menu-list-item";
19518             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19519             item.render(li, this);
19520             this.delayAutoWidth();
19521         }
19522         return item;
19523     },
19524
19525     /**
19526      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19527      * @param {Roo.menu.Item} item The menu item to remove
19528      */
19529     remove : function(item){
19530         this.items.removeKey(item.id);
19531         item.destroy();
19532     },
19533
19534     /**
19535      * Removes and destroys all items in the menu
19536      */
19537     removeAll : function(){
19538         var f;
19539         while(f = this.items.first()){
19540             this.remove(f);
19541         }
19542     }
19543 });
19544
19545 // MenuNav is a private utility class used internally by the Menu
19546 Roo.menu.MenuNav = function(menu){
19547     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19548     this.scope = this.menu = menu;
19549 };
19550
19551 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19552     doRelay : function(e, h){
19553         var k = e.getKey();
19554         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19555             this.menu.tryActivate(0, 1);
19556             return false;
19557         }
19558         return h.call(this.scope || this, e, this.menu);
19559     },
19560
19561     up : function(e, m){
19562         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19563             m.tryActivate(m.items.length-1, -1);
19564         }
19565     },
19566
19567     down : function(e, m){
19568         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19569             m.tryActivate(0, 1);
19570         }
19571     },
19572
19573     right : function(e, m){
19574         if(m.activeItem){
19575             m.activeItem.expandMenu(true);
19576         }
19577     },
19578
19579     left : function(e, m){
19580         m.hide();
19581         if(m.parentMenu && m.parentMenu.activeItem){
19582             m.parentMenu.activeItem.activate();
19583         }
19584     },
19585
19586     enter : function(e, m){
19587         if(m.activeItem){
19588             e.stopPropagation();
19589             m.activeItem.onClick(e);
19590             m.fireEvent("click", this, m.activeItem);
19591             return true;
19592         }
19593     }
19594 });/*
19595  * Based on:
19596  * Ext JS Library 1.1.1
19597  * Copyright(c) 2006-2007, Ext JS, LLC.
19598  *
19599  * Originally Released Under LGPL - original licence link has changed is not relivant.
19600  *
19601  * Fork - LGPL
19602  * <script type="text/javascript">
19603  */
19604  
19605 /**
19606  * @class Roo.menu.MenuMgr
19607  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19608  * @singleton
19609  */
19610 Roo.menu.MenuMgr = function(){
19611    var menus, active, groups = {}, attached = false, lastShow = new Date();
19612
19613    // private - called when first menu is created
19614    function init(){
19615        menus = {};
19616        active = new Roo.util.MixedCollection();
19617        Roo.get(document).addKeyListener(27, function(){
19618            if(active.length > 0){
19619                hideAll();
19620            }
19621        });
19622    }
19623
19624    // private
19625    function hideAll(){
19626        if(active && active.length > 0){
19627            var c = active.clone();
19628            c.each(function(m){
19629                m.hide();
19630            });
19631        }
19632    }
19633
19634    // private
19635    function onHide(m){
19636        active.remove(m);
19637        if(active.length < 1){
19638            Roo.get(document).un("mousedown", onMouseDown);
19639            attached = false;
19640        }
19641    }
19642
19643    // private
19644    function onShow(m){
19645        var last = active.last();
19646        lastShow = new Date();
19647        active.add(m);
19648        if(!attached){
19649            Roo.get(document).on("mousedown", onMouseDown);
19650            attached = true;
19651        }
19652        if(m.parentMenu){
19653           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19654           m.parentMenu.activeChild = m;
19655        }else if(last && last.isVisible()){
19656           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19657        }
19658    }
19659
19660    // private
19661    function onBeforeHide(m){
19662        if(m.activeChild){
19663            m.activeChild.hide();
19664        }
19665        if(m.autoHideTimer){
19666            clearTimeout(m.autoHideTimer);
19667            delete m.autoHideTimer;
19668        }
19669    }
19670
19671    // private
19672    function onBeforeShow(m){
19673        var pm = m.parentMenu;
19674        if(!pm && !m.allowOtherMenus){
19675            hideAll();
19676        }else if(pm && pm.activeChild && active != m){
19677            pm.activeChild.hide();
19678        }
19679    }
19680
19681    // private
19682    function onMouseDown(e){
19683        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19684            hideAll();
19685        }
19686    }
19687
19688    // private
19689    function onBeforeCheck(mi, state){
19690        if(state){
19691            var g = groups[mi.group];
19692            for(var i = 0, l = g.length; i < l; i++){
19693                if(g[i] != mi){
19694                    g[i].setChecked(false);
19695                }
19696            }
19697        }
19698    }
19699
19700    return {
19701
19702        /**
19703         * Hides all menus that are currently visible
19704         */
19705        hideAll : function(){
19706             hideAll();  
19707        },
19708
19709        // private
19710        register : function(menu){
19711            if(!menus){
19712                init();
19713            }
19714            menus[menu.id] = menu;
19715            menu.on("beforehide", onBeforeHide);
19716            menu.on("hide", onHide);
19717            menu.on("beforeshow", onBeforeShow);
19718            menu.on("show", onShow);
19719            var g = menu.group;
19720            if(g && menu.events["checkchange"]){
19721                if(!groups[g]){
19722                    groups[g] = [];
19723                }
19724                groups[g].push(menu);
19725                menu.on("checkchange", onCheck);
19726            }
19727        },
19728
19729         /**
19730          * Returns a {@link Roo.menu.Menu} object
19731          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19732          * be used to generate and return a new Menu instance.
19733          */
19734        get : function(menu){
19735            if(typeof menu == "string"){ // menu id
19736                return menus[menu];
19737            }else if(menu.events){  // menu instance
19738                return menu;
19739            }else if(typeof menu.length == 'number'){ // array of menu items?
19740                return new Roo.menu.Menu({items:menu});
19741            }else{ // otherwise, must be a config
19742                return new Roo.menu.Menu(menu);
19743            }
19744        },
19745
19746        // private
19747        unregister : function(menu){
19748            delete menus[menu.id];
19749            menu.un("beforehide", onBeforeHide);
19750            menu.un("hide", onHide);
19751            menu.un("beforeshow", onBeforeShow);
19752            menu.un("show", onShow);
19753            var g = menu.group;
19754            if(g && menu.events["checkchange"]){
19755                groups[g].remove(menu);
19756                menu.un("checkchange", onCheck);
19757            }
19758        },
19759
19760        // private
19761        registerCheckable : function(menuItem){
19762            var g = menuItem.group;
19763            if(g){
19764                if(!groups[g]){
19765                    groups[g] = [];
19766                }
19767                groups[g].push(menuItem);
19768                menuItem.on("beforecheckchange", onBeforeCheck);
19769            }
19770        },
19771
19772        // private
19773        unregisterCheckable : function(menuItem){
19774            var g = menuItem.group;
19775            if(g){
19776                groups[g].remove(menuItem);
19777                menuItem.un("beforecheckchange", onBeforeCheck);
19778            }
19779        }
19780    };
19781 }();/*
19782  * Based on:
19783  * Ext JS Library 1.1.1
19784  * Copyright(c) 2006-2007, Ext JS, LLC.
19785  *
19786  * Originally Released Under LGPL - original licence link has changed is not relivant.
19787  *
19788  * Fork - LGPL
19789  * <script type="text/javascript">
19790  */
19791  
19792
19793 /**
19794  * @class Roo.menu.BaseItem
19795  * @extends Roo.Component
19796  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19797  * management and base configuration options shared by all menu components.
19798  * @constructor
19799  * Creates a new BaseItem
19800  * @param {Object} config Configuration options
19801  */
19802 Roo.menu.BaseItem = function(config){
19803     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19804
19805     this.addEvents({
19806         /**
19807          * @event click
19808          * Fires when this item is clicked
19809          * @param {Roo.menu.BaseItem} this
19810          * @param {Roo.EventObject} e
19811          */
19812         click: true,
19813         /**
19814          * @event activate
19815          * Fires when this item is activated
19816          * @param {Roo.menu.BaseItem} this
19817          */
19818         activate : true,
19819         /**
19820          * @event deactivate
19821          * Fires when this item is deactivated
19822          * @param {Roo.menu.BaseItem} this
19823          */
19824         deactivate : true
19825     });
19826
19827     if(this.handler){
19828         this.on("click", this.handler, this.scope, true);
19829     }
19830 };
19831
19832 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19833     /**
19834      * @cfg {Function} handler
19835      * A function that will handle the click event of this menu item (defaults to undefined)
19836      */
19837     /**
19838      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19839      */
19840     canActivate : false,
19841     
19842      /**
19843      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19844      */
19845     hidden: false,
19846     
19847     /**
19848      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19849      */
19850     activeClass : "x-menu-item-active",
19851     /**
19852      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19853      */
19854     hideOnClick : true,
19855     /**
19856      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19857      */
19858     hideDelay : 100,
19859
19860     // private
19861     ctype: "Roo.menu.BaseItem",
19862
19863     // private
19864     actionMode : "container",
19865
19866     // private
19867     render : function(container, parentMenu){
19868         this.parentMenu = parentMenu;
19869         Roo.menu.BaseItem.superclass.render.call(this, container);
19870         this.container.menuItemId = this.id;
19871     },
19872
19873     // private
19874     onRender : function(container, position){
19875         this.el = Roo.get(this.el);
19876         container.dom.appendChild(this.el.dom);
19877     },
19878
19879     // private
19880     onClick : function(e){
19881         if(!this.disabled && this.fireEvent("click", this, e) !== false
19882                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19883             this.handleClick(e);
19884         }else{
19885             e.stopEvent();
19886         }
19887     },
19888
19889     // private
19890     activate : function(){
19891         if(this.disabled){
19892             return false;
19893         }
19894         var li = this.container;
19895         li.addClass(this.activeClass);
19896         this.region = li.getRegion().adjust(2, 2, -2, -2);
19897         this.fireEvent("activate", this);
19898         return true;
19899     },
19900
19901     // private
19902     deactivate : function(){
19903         this.container.removeClass(this.activeClass);
19904         this.fireEvent("deactivate", this);
19905     },
19906
19907     // private
19908     shouldDeactivate : function(e){
19909         return !this.region || !this.region.contains(e.getPoint());
19910     },
19911
19912     // private
19913     handleClick : function(e){
19914         if(this.hideOnClick){
19915             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19916         }
19917     },
19918
19919     // private
19920     expandMenu : function(autoActivate){
19921         // do nothing
19922     },
19923
19924     // private
19925     hideMenu : function(){
19926         // do nothing
19927     }
19928 });/*
19929  * Based on:
19930  * Ext JS Library 1.1.1
19931  * Copyright(c) 2006-2007, Ext JS, LLC.
19932  *
19933  * Originally Released Under LGPL - original licence link has changed is not relivant.
19934  *
19935  * Fork - LGPL
19936  * <script type="text/javascript">
19937  */
19938  
19939 /**
19940  * @class Roo.menu.Adapter
19941  * @extends Roo.menu.BaseItem
19942  * 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.
19943  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
19944  * @constructor
19945  * Creates a new Adapter
19946  * @param {Object} config Configuration options
19947  */
19948 Roo.menu.Adapter = function(component, config){
19949     Roo.menu.Adapter.superclass.constructor.call(this, config);
19950     this.component = component;
19951 };
19952 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
19953     // private
19954     canActivate : true,
19955
19956     // private
19957     onRender : function(container, position){
19958         this.component.render(container);
19959         this.el = this.component.getEl();
19960     },
19961
19962     // private
19963     activate : function(){
19964         if(this.disabled){
19965             return false;
19966         }
19967         this.component.focus();
19968         this.fireEvent("activate", this);
19969         return true;
19970     },
19971
19972     // private
19973     deactivate : function(){
19974         this.fireEvent("deactivate", this);
19975     },
19976
19977     // private
19978     disable : function(){
19979         this.component.disable();
19980         Roo.menu.Adapter.superclass.disable.call(this);
19981     },
19982
19983     // private
19984     enable : function(){
19985         this.component.enable();
19986         Roo.menu.Adapter.superclass.enable.call(this);
19987     }
19988 });/*
19989  * Based on:
19990  * Ext JS Library 1.1.1
19991  * Copyright(c) 2006-2007, Ext JS, LLC.
19992  *
19993  * Originally Released Under LGPL - original licence link has changed is not relivant.
19994  *
19995  * Fork - LGPL
19996  * <script type="text/javascript">
19997  */
19998
19999 /**
20000  * @class Roo.menu.TextItem
20001  * @extends Roo.menu.BaseItem
20002  * Adds a static text string to a menu, usually used as either a heading or group separator.
20003  * Note: old style constructor with text is still supported.
20004  * 
20005  * @constructor
20006  * Creates a new TextItem
20007  * @param {Object} cfg Configuration
20008  */
20009 Roo.menu.TextItem = function(cfg){
20010     if (typeof(cfg) == 'string') {
20011         this.text = cfg;
20012     } else {
20013         Roo.apply(this,cfg);
20014     }
20015     
20016     Roo.menu.TextItem.superclass.constructor.call(this);
20017 };
20018
20019 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20020     /**
20021      * @cfg {Boolean} text Text to show on item.
20022      */
20023     text : '',
20024     
20025     /**
20026      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20027      */
20028     hideOnClick : false,
20029     /**
20030      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20031      */
20032     itemCls : "x-menu-text",
20033
20034     // private
20035     onRender : function(){
20036         var s = document.createElement("span");
20037         s.className = this.itemCls;
20038         s.innerHTML = this.text;
20039         this.el = s;
20040         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20041     }
20042 });/*
20043  * Based on:
20044  * Ext JS Library 1.1.1
20045  * Copyright(c) 2006-2007, Ext JS, LLC.
20046  *
20047  * Originally Released Under LGPL - original licence link has changed is not relivant.
20048  *
20049  * Fork - LGPL
20050  * <script type="text/javascript">
20051  */
20052
20053 /**
20054  * @class Roo.menu.Separator
20055  * @extends Roo.menu.BaseItem
20056  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20057  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20058  * @constructor
20059  * @param {Object} config Configuration options
20060  */
20061 Roo.menu.Separator = function(config){
20062     Roo.menu.Separator.superclass.constructor.call(this, config);
20063 };
20064
20065 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20066     /**
20067      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20068      */
20069     itemCls : "x-menu-sep",
20070     /**
20071      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20072      */
20073     hideOnClick : false,
20074
20075     // private
20076     onRender : function(li){
20077         var s = document.createElement("span");
20078         s.className = this.itemCls;
20079         s.innerHTML = "&#160;";
20080         this.el = s;
20081         li.addClass("x-menu-sep-li");
20082         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20083     }
20084 });/*
20085  * Based on:
20086  * Ext JS Library 1.1.1
20087  * Copyright(c) 2006-2007, Ext JS, LLC.
20088  *
20089  * Originally Released Under LGPL - original licence link has changed is not relivant.
20090  *
20091  * Fork - LGPL
20092  * <script type="text/javascript">
20093  */
20094 /**
20095  * @class Roo.menu.Item
20096  * @extends Roo.menu.BaseItem
20097  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20098  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20099  * activation and click handling.
20100  * @constructor
20101  * Creates a new Item
20102  * @param {Object} config Configuration options
20103  */
20104 Roo.menu.Item = function(config){
20105     Roo.menu.Item.superclass.constructor.call(this, config);
20106     if(this.menu){
20107         this.menu = Roo.menu.MenuMgr.get(this.menu);
20108     }
20109 };
20110 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20111     
20112     /**
20113      * @cfg {String} text
20114      * The text to show on the menu item.
20115      */
20116     text: '',
20117      /**
20118      * @cfg {String} HTML to render in menu
20119      * The text to show on the menu item (HTML version).
20120      */
20121     html: '',
20122     /**
20123      * @cfg {String} icon
20124      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20125      */
20126     icon: undefined,
20127     /**
20128      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20129      */
20130     itemCls : "x-menu-item",
20131     /**
20132      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20133      */
20134     canActivate : true,
20135     /**
20136      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20137      */
20138     showDelay: 200,
20139     // doc'd in BaseItem
20140     hideDelay: 200,
20141
20142     // private
20143     ctype: "Roo.menu.Item",
20144     
20145     // private
20146     onRender : function(container, position){
20147         var el = document.createElement("a");
20148         el.hideFocus = true;
20149         el.unselectable = "on";
20150         el.href = this.href || "#";
20151         if(this.hrefTarget){
20152             el.target = this.hrefTarget;
20153         }
20154         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20155         
20156         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20157         
20158         el.innerHTML = String.format(
20159                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20160                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20161         this.el = el;
20162         Roo.menu.Item.superclass.onRender.call(this, container, position);
20163     },
20164
20165     /**
20166      * Sets the text to display in this menu item
20167      * @param {String} text The text to display
20168      * @param {Boolean} isHTML true to indicate text is pure html.
20169      */
20170     setText : function(text, isHTML){
20171         if (isHTML) {
20172             this.html = text;
20173         } else {
20174             this.text = text;
20175             this.html = '';
20176         }
20177         if(this.rendered){
20178             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20179      
20180             this.el.update(String.format(
20181                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20182                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20183             this.parentMenu.autoWidth();
20184         }
20185     },
20186
20187     // private
20188     handleClick : function(e){
20189         if(!this.href){ // if no link defined, stop the event automatically
20190             e.stopEvent();
20191         }
20192         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20193     },
20194
20195     // private
20196     activate : function(autoExpand){
20197         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20198             this.focus();
20199             if(autoExpand){
20200                 this.expandMenu();
20201             }
20202         }
20203         return true;
20204     },
20205
20206     // private
20207     shouldDeactivate : function(e){
20208         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20209             if(this.menu && this.menu.isVisible()){
20210                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20211             }
20212             return true;
20213         }
20214         return false;
20215     },
20216
20217     // private
20218     deactivate : function(){
20219         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20220         this.hideMenu();
20221     },
20222
20223     // private
20224     expandMenu : function(autoActivate){
20225         if(!this.disabled && this.menu){
20226             clearTimeout(this.hideTimer);
20227             delete this.hideTimer;
20228             if(!this.menu.isVisible() && !this.showTimer){
20229                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20230             }else if (this.menu.isVisible() && autoActivate){
20231                 this.menu.tryActivate(0, 1);
20232             }
20233         }
20234     },
20235
20236     // private
20237     deferExpand : function(autoActivate){
20238         delete this.showTimer;
20239         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20240         if(autoActivate){
20241             this.menu.tryActivate(0, 1);
20242         }
20243     },
20244
20245     // private
20246     hideMenu : function(){
20247         clearTimeout(this.showTimer);
20248         delete this.showTimer;
20249         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20250             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20251         }
20252     },
20253
20254     // private
20255     deferHide : function(){
20256         delete this.hideTimer;
20257         this.menu.hide();
20258     }
20259 });/*
20260  * Based on:
20261  * Ext JS Library 1.1.1
20262  * Copyright(c) 2006-2007, Ext JS, LLC.
20263  *
20264  * Originally Released Under LGPL - original licence link has changed is not relivant.
20265  *
20266  * Fork - LGPL
20267  * <script type="text/javascript">
20268  */
20269  
20270 /**
20271  * @class Roo.menu.CheckItem
20272  * @extends Roo.menu.Item
20273  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20274  * @constructor
20275  * Creates a new CheckItem
20276  * @param {Object} config Configuration options
20277  */
20278 Roo.menu.CheckItem = function(config){
20279     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20280     this.addEvents({
20281         /**
20282          * @event beforecheckchange
20283          * Fires before the checked value is set, providing an opportunity to cancel if needed
20284          * @param {Roo.menu.CheckItem} this
20285          * @param {Boolean} checked The new checked value that will be set
20286          */
20287         "beforecheckchange" : true,
20288         /**
20289          * @event checkchange
20290          * Fires after the checked value has been set
20291          * @param {Roo.menu.CheckItem} this
20292          * @param {Boolean} checked The checked value that was set
20293          */
20294         "checkchange" : true
20295     });
20296     if(this.checkHandler){
20297         this.on('checkchange', this.checkHandler, this.scope);
20298     }
20299 };
20300 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20301     /**
20302      * @cfg {String} group
20303      * All check items with the same group name will automatically be grouped into a single-select
20304      * radio button group (defaults to '')
20305      */
20306     /**
20307      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20308      */
20309     itemCls : "x-menu-item x-menu-check-item",
20310     /**
20311      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20312      */
20313     groupClass : "x-menu-group-item",
20314
20315     /**
20316      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20317      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20318      * initialized with checked = true will be rendered as checked.
20319      */
20320     checked: false,
20321
20322     // private
20323     ctype: "Roo.menu.CheckItem",
20324
20325     // private
20326     onRender : function(c){
20327         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20328         if(this.group){
20329             this.el.addClass(this.groupClass);
20330         }
20331         Roo.menu.MenuMgr.registerCheckable(this);
20332         if(this.checked){
20333             this.checked = false;
20334             this.setChecked(true, true);
20335         }
20336     },
20337
20338     // private
20339     destroy : function(){
20340         if(this.rendered){
20341             Roo.menu.MenuMgr.unregisterCheckable(this);
20342         }
20343         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20344     },
20345
20346     /**
20347      * Set the checked state of this item
20348      * @param {Boolean} checked The new checked value
20349      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20350      */
20351     setChecked : function(state, suppressEvent){
20352         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20353             if(this.container){
20354                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20355             }
20356             this.checked = state;
20357             if(suppressEvent !== true){
20358                 this.fireEvent("checkchange", this, state);
20359             }
20360         }
20361     },
20362
20363     // private
20364     handleClick : function(e){
20365        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20366            this.setChecked(!this.checked);
20367        }
20368        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20369     }
20370 });/*
20371  * Based on:
20372  * Ext JS Library 1.1.1
20373  * Copyright(c) 2006-2007, Ext JS, LLC.
20374  *
20375  * Originally Released Under LGPL - original licence link has changed is not relivant.
20376  *
20377  * Fork - LGPL
20378  * <script type="text/javascript">
20379  */
20380  
20381 /**
20382  * @class Roo.menu.DateItem
20383  * @extends Roo.menu.Adapter
20384  * A menu item that wraps the {@link Roo.DatPicker} component.
20385  * @constructor
20386  * Creates a new DateItem
20387  * @param {Object} config Configuration options
20388  */
20389 Roo.menu.DateItem = function(config){
20390     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20391     /** The Roo.DatePicker object @type Roo.DatePicker */
20392     this.picker = this.component;
20393     this.addEvents({select: true});
20394     
20395     this.picker.on("render", function(picker){
20396         picker.getEl().swallowEvent("click");
20397         picker.container.addClass("x-menu-date-item");
20398     });
20399
20400     this.picker.on("select", this.onSelect, this);
20401 };
20402
20403 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20404     // private
20405     onSelect : function(picker, date){
20406         this.fireEvent("select", this, date, picker);
20407         Roo.menu.DateItem.superclass.handleClick.call(this);
20408     }
20409 });/*
20410  * Based on:
20411  * Ext JS Library 1.1.1
20412  * Copyright(c) 2006-2007, Ext JS, LLC.
20413  *
20414  * Originally Released Under LGPL - original licence link has changed is not relivant.
20415  *
20416  * Fork - LGPL
20417  * <script type="text/javascript">
20418  */
20419  
20420 /**
20421  * @class Roo.menu.ColorItem
20422  * @extends Roo.menu.Adapter
20423  * A menu item that wraps the {@link Roo.ColorPalette} component.
20424  * @constructor
20425  * Creates a new ColorItem
20426  * @param {Object} config Configuration options
20427  */
20428 Roo.menu.ColorItem = function(config){
20429     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20430     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20431     this.palette = this.component;
20432     this.relayEvents(this.palette, ["select"]);
20433     if(this.selectHandler){
20434         this.on('select', this.selectHandler, this.scope);
20435     }
20436 };
20437 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20438  * Based on:
20439  * Ext JS Library 1.1.1
20440  * Copyright(c) 2006-2007, Ext JS, LLC.
20441  *
20442  * Originally Released Under LGPL - original licence link has changed is not relivant.
20443  *
20444  * Fork - LGPL
20445  * <script type="text/javascript">
20446  */
20447  
20448
20449 /**
20450  * @class Roo.menu.DateMenu
20451  * @extends Roo.menu.Menu
20452  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20453  * @constructor
20454  * Creates a new DateMenu
20455  * @param {Object} config Configuration options
20456  */
20457 Roo.menu.DateMenu = function(config){
20458     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20459     this.plain = true;
20460     var di = new Roo.menu.DateItem(config);
20461     this.add(di);
20462     /**
20463      * The {@link Roo.DatePicker} instance for this DateMenu
20464      * @type DatePicker
20465      */
20466     this.picker = di.picker;
20467     /**
20468      * @event select
20469      * @param {DatePicker} picker
20470      * @param {Date} date
20471      */
20472     this.relayEvents(di, ["select"]);
20473     this.on('beforeshow', function(){
20474         if(this.picker){
20475             this.picker.hideMonthPicker(false);
20476         }
20477     }, this);
20478 };
20479 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20480     cls:'x-date-menu'
20481 });/*
20482  * Based on:
20483  * Ext JS Library 1.1.1
20484  * Copyright(c) 2006-2007, Ext JS, LLC.
20485  *
20486  * Originally Released Under LGPL - original licence link has changed is not relivant.
20487  *
20488  * Fork - LGPL
20489  * <script type="text/javascript">
20490  */
20491  
20492
20493 /**
20494  * @class Roo.menu.ColorMenu
20495  * @extends Roo.menu.Menu
20496  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20497  * @constructor
20498  * Creates a new ColorMenu
20499  * @param {Object} config Configuration options
20500  */
20501 Roo.menu.ColorMenu = function(config){
20502     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20503     this.plain = true;
20504     var ci = new Roo.menu.ColorItem(config);
20505     this.add(ci);
20506     /**
20507      * The {@link Roo.ColorPalette} instance for this ColorMenu
20508      * @type ColorPalette
20509      */
20510     this.palette = ci.palette;
20511     /**
20512      * @event select
20513      * @param {ColorPalette} palette
20514      * @param {String} color
20515      */
20516     this.relayEvents(ci, ["select"]);
20517 };
20518 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20519  * Based on:
20520  * Ext JS Library 1.1.1
20521  * Copyright(c) 2006-2007, Ext JS, LLC.
20522  *
20523  * Originally Released Under LGPL - original licence link has changed is not relivant.
20524  *
20525  * Fork - LGPL
20526  * <script type="text/javascript">
20527  */
20528  
20529 /**
20530  * @class Roo.form.Field
20531  * @extends Roo.BoxComponent
20532  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20533  * @constructor
20534  * Creates a new Field
20535  * @param {Object} config Configuration options
20536  */
20537 Roo.form.Field = function(config){
20538     Roo.form.Field.superclass.constructor.call(this, config);
20539 };
20540
20541 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20542     /**
20543      * @cfg {String} fieldLabel Label to use when rendering a form.
20544      */
20545        /**
20546      * @cfg {String} qtip Mouse over tip
20547      */
20548      
20549     /**
20550      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20551      */
20552     invalidClass : "x-form-invalid",
20553     /**
20554      * @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")
20555      */
20556     invalidText : "The value in this field is invalid",
20557     /**
20558      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20559      */
20560     focusClass : "x-form-focus",
20561     /**
20562      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20563       automatic validation (defaults to "keyup").
20564      */
20565     validationEvent : "keyup",
20566     /**
20567      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20568      */
20569     validateOnBlur : true,
20570     /**
20571      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20572      */
20573     validationDelay : 250,
20574     /**
20575      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20576      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20577      */
20578     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20579     /**
20580      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20581      */
20582     fieldClass : "x-form-field",
20583     /**
20584      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20585      *<pre>
20586 Value         Description
20587 -----------   ----------------------------------------------------------------------
20588 qtip          Display a quick tip when the user hovers over the field
20589 title         Display a default browser title attribute popup
20590 under         Add a block div beneath the field containing the error text
20591 side          Add an error icon to the right of the field with a popup on hover
20592 [element id]  Add the error text directly to the innerHTML of the specified element
20593 </pre>
20594      */
20595     msgTarget : 'qtip',
20596     /**
20597      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20598      */
20599     msgFx : 'normal',
20600
20601     /**
20602      * @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.
20603      */
20604     readOnly : false,
20605
20606     /**
20607      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20608      */
20609     disabled : false,
20610
20611     /**
20612      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20613      */
20614     inputType : undefined,
20615     
20616     /**
20617      * @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).
20618          */
20619         tabIndex : undefined,
20620         
20621     // private
20622     isFormField : true,
20623
20624     // private
20625     hasFocus : false,
20626     /**
20627      * @property {Roo.Element} fieldEl
20628      * Element Containing the rendered Field (with label etc.)
20629      */
20630     /**
20631      * @cfg {Mixed} value A value to initialize this field with.
20632      */
20633     value : undefined,
20634
20635     /**
20636      * @cfg {String} name The field's HTML name attribute.
20637      */
20638     /**
20639      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20640      */
20641
20642         // private ??
20643         initComponent : function(){
20644         Roo.form.Field.superclass.initComponent.call(this);
20645         this.addEvents({
20646             /**
20647              * @event focus
20648              * Fires when this field receives input focus.
20649              * @param {Roo.form.Field} this
20650              */
20651             focus : true,
20652             /**
20653              * @event blur
20654              * Fires when this field loses input focus.
20655              * @param {Roo.form.Field} this
20656              */
20657             blur : true,
20658             /**
20659              * @event specialkey
20660              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20661              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20662              * @param {Roo.form.Field} this
20663              * @param {Roo.EventObject} e The event object
20664              */
20665             specialkey : true,
20666             /**
20667              * @event change
20668              * Fires just before the field blurs if the field value has changed.
20669              * @param {Roo.form.Field} this
20670              * @param {Mixed} newValue The new value
20671              * @param {Mixed} oldValue The original value
20672              */
20673             change : true,
20674             /**
20675              * @event invalid
20676              * Fires after the field has been marked as invalid.
20677              * @param {Roo.form.Field} this
20678              * @param {String} msg The validation message
20679              */
20680             invalid : true,
20681             /**
20682              * @event valid
20683              * Fires after the field has been validated with no errors.
20684              * @param {Roo.form.Field} this
20685              */
20686             valid : true,
20687              /**
20688              * @event keyup
20689              * Fires after the key up
20690              * @param {Roo.form.Field} this
20691              * @param {Roo.EventObject}  e The event Object
20692              */
20693             keyup : true
20694         });
20695     },
20696
20697     /**
20698      * Returns the name attribute of the field if available
20699      * @return {String} name The field name
20700      */
20701     getName: function(){
20702          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20703     },
20704
20705     // private
20706     onRender : function(ct, position){
20707         Roo.form.Field.superclass.onRender.call(this, ct, position);
20708         if(!this.el){
20709             var cfg = this.getAutoCreate();
20710             if(!cfg.name){
20711                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20712             }
20713             if (!cfg.name.length) {
20714                 delete cfg.name;
20715             }
20716             if(this.inputType){
20717                 cfg.type = this.inputType;
20718             }
20719             this.el = ct.createChild(cfg, position);
20720         }
20721         var type = this.el.dom.type;
20722         if(type){
20723             if(type == 'password'){
20724                 type = 'text';
20725             }
20726             this.el.addClass('x-form-'+type);
20727         }
20728         if(this.readOnly){
20729             this.el.dom.readOnly = true;
20730         }
20731         if(this.tabIndex !== undefined){
20732             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20733         }
20734
20735         this.el.addClass([this.fieldClass, this.cls]);
20736         this.initValue();
20737     },
20738
20739     /**
20740      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20741      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20742      * @return {Roo.form.Field} this
20743      */
20744     applyTo : function(target){
20745         this.allowDomMove = false;
20746         this.el = Roo.get(target);
20747         this.render(this.el.dom.parentNode);
20748         return this;
20749     },
20750
20751     // private
20752     initValue : function(){
20753         if(this.value !== undefined){
20754             this.setValue(this.value);
20755         }else if(this.el.dom.value.length > 0){
20756             this.setValue(this.el.dom.value);
20757         }
20758     },
20759
20760     /**
20761      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20762      */
20763     isDirty : function() {
20764         if(this.disabled) {
20765             return false;
20766         }
20767         return String(this.getValue()) !== String(this.originalValue);
20768     },
20769
20770     // private
20771     afterRender : function(){
20772         Roo.form.Field.superclass.afterRender.call(this);
20773         this.initEvents();
20774     },
20775
20776     // private
20777     fireKey : function(e){
20778         //Roo.log('field ' + e.getKey());
20779         if(e.isNavKeyPress()){
20780             this.fireEvent("specialkey", this, e);
20781         }
20782     },
20783
20784     /**
20785      * Resets the current field value to the originally loaded value and clears any validation messages
20786      */
20787     reset : function(){
20788         this.setValue(this.resetValue);
20789         this.clearInvalid();
20790     },
20791
20792     // private
20793     initEvents : function(){
20794         // safari killled keypress - so keydown is now used..
20795         this.el.on("keydown" , this.fireKey,  this);
20796         this.el.on("focus", this.onFocus,  this);
20797         this.el.on("blur", this.onBlur,  this);
20798         this.el.relayEvent('keyup', this);
20799
20800         // reference to original value for reset
20801         this.originalValue = this.getValue();
20802         this.resetValue =  this.getValue();
20803     },
20804
20805     // private
20806     onFocus : function(){
20807         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20808             this.el.addClass(this.focusClass);
20809         }
20810         if(!this.hasFocus){
20811             this.hasFocus = true;
20812             this.startValue = this.getValue();
20813             this.fireEvent("focus", this);
20814         }
20815     },
20816
20817     beforeBlur : Roo.emptyFn,
20818
20819     // private
20820     onBlur : function(){
20821         this.beforeBlur();
20822         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20823             this.el.removeClass(this.focusClass);
20824         }
20825         this.hasFocus = false;
20826         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20827             this.validate();
20828         }
20829         var v = this.getValue();
20830         if(String(v) !== String(this.startValue)){
20831             this.fireEvent('change', this, v, this.startValue);
20832         }
20833         this.fireEvent("blur", this);
20834     },
20835
20836     /**
20837      * Returns whether or not the field value is currently valid
20838      * @param {Boolean} preventMark True to disable marking the field invalid
20839      * @return {Boolean} True if the value is valid, else false
20840      */
20841     isValid : function(preventMark){
20842         if(this.disabled){
20843             return true;
20844         }
20845         var restore = this.preventMark;
20846         this.preventMark = preventMark === true;
20847         var v = this.validateValue(this.processValue(this.getRawValue()));
20848         this.preventMark = restore;
20849         return v;
20850     },
20851
20852     /**
20853      * Validates the field value
20854      * @return {Boolean} True if the value is valid, else false
20855      */
20856     validate : function(){
20857         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20858             this.clearInvalid();
20859             return true;
20860         }
20861         return false;
20862     },
20863
20864     processValue : function(value){
20865         return value;
20866     },
20867
20868     // private
20869     // Subclasses should provide the validation implementation by overriding this
20870     validateValue : function(value){
20871         return true;
20872     },
20873
20874     /**
20875      * Mark this field as invalid
20876      * @param {String} msg The validation message
20877      */
20878     markInvalid : function(msg){
20879         if(!this.rendered || this.preventMark){ // not rendered
20880             return;
20881         }
20882         
20883         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20884         
20885         obj.el.addClass(this.invalidClass);
20886         msg = msg || this.invalidText;
20887         switch(this.msgTarget){
20888             case 'qtip':
20889                 obj.el.dom.qtip = msg;
20890                 obj.el.dom.qclass = 'x-form-invalid-tip';
20891                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20892                     Roo.QuickTips.enable();
20893                 }
20894                 break;
20895             case 'title':
20896                 this.el.dom.title = msg;
20897                 break;
20898             case 'under':
20899                 if(!this.errorEl){
20900                     var elp = this.el.findParent('.x-form-element', 5, true);
20901                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20902                     this.errorEl.setWidth(elp.getWidth(true)-20);
20903                 }
20904                 this.errorEl.update(msg);
20905                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20906                 break;
20907             case 'side':
20908                 if(!this.errorIcon){
20909                     var elp = this.el.findParent('.x-form-element', 5, true);
20910                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20911                 }
20912                 this.alignErrorIcon();
20913                 this.errorIcon.dom.qtip = msg;
20914                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20915                 this.errorIcon.show();
20916                 this.on('resize', this.alignErrorIcon, this);
20917                 break;
20918             default:
20919                 var t = Roo.getDom(this.msgTarget);
20920                 t.innerHTML = msg;
20921                 t.style.display = this.msgDisplay;
20922                 break;
20923         }
20924         this.fireEvent('invalid', this, msg);
20925     },
20926
20927     // private
20928     alignErrorIcon : function(){
20929         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20930     },
20931
20932     /**
20933      * Clear any invalid styles/messages for this field
20934      */
20935     clearInvalid : function(){
20936         if(!this.rendered || this.preventMark){ // not rendered
20937             return;
20938         }
20939         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20940         
20941         obj.el.removeClass(this.invalidClass);
20942         switch(this.msgTarget){
20943             case 'qtip':
20944                 obj.el.dom.qtip = '';
20945                 break;
20946             case 'title':
20947                 this.el.dom.title = '';
20948                 break;
20949             case 'under':
20950                 if(this.errorEl){
20951                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
20952                 }
20953                 break;
20954             case 'side':
20955                 if(this.errorIcon){
20956                     this.errorIcon.dom.qtip = '';
20957                     this.errorIcon.hide();
20958                     this.un('resize', this.alignErrorIcon, this);
20959                 }
20960                 break;
20961             default:
20962                 var t = Roo.getDom(this.msgTarget);
20963                 t.innerHTML = '';
20964                 t.style.display = 'none';
20965                 break;
20966         }
20967         this.fireEvent('valid', this);
20968     },
20969
20970     /**
20971      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
20972      * @return {Mixed} value The field value
20973      */
20974     getRawValue : function(){
20975         var v = this.el.getValue();
20976         
20977         return v;
20978     },
20979
20980     /**
20981      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20982      * @return {Mixed} value The field value
20983      */
20984     getValue : function(){
20985         var v = this.el.getValue();
20986          
20987         return v;
20988     },
20989
20990     /**
20991      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
20992      * @param {Mixed} value The value to set
20993      */
20994     setRawValue : function(v){
20995         return this.el.dom.value = (v === null || v === undefined ? '' : v);
20996     },
20997
20998     /**
20999      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21000      * @param {Mixed} value The value to set
21001      */
21002     setValue : function(v){
21003         this.value = v;
21004         if(this.rendered){
21005             this.el.dom.value = (v === null || v === undefined ? '' : v);
21006              this.validate();
21007         }
21008     },
21009
21010     adjustSize : function(w, h){
21011         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21012         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21013         return s;
21014     },
21015
21016     adjustWidth : function(tag, w){
21017         tag = tag.toLowerCase();
21018         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21019             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21020                 if(tag == 'input'){
21021                     return w + 2;
21022                 }
21023                 if(tag == 'textarea'){
21024                     return w-2;
21025                 }
21026             }else if(Roo.isOpera){
21027                 if(tag == 'input'){
21028                     return w + 2;
21029                 }
21030                 if(tag == 'textarea'){
21031                     return w-2;
21032                 }
21033             }
21034         }
21035         return w;
21036     }
21037 });
21038
21039
21040 // anything other than normal should be considered experimental
21041 Roo.form.Field.msgFx = {
21042     normal : {
21043         show: function(msgEl, f){
21044             msgEl.setDisplayed('block');
21045         },
21046
21047         hide : function(msgEl, f){
21048             msgEl.setDisplayed(false).update('');
21049         }
21050     },
21051
21052     slide : {
21053         show: function(msgEl, f){
21054             msgEl.slideIn('t', {stopFx:true});
21055         },
21056
21057         hide : function(msgEl, f){
21058             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21059         }
21060     },
21061
21062     slideRight : {
21063         show: function(msgEl, f){
21064             msgEl.fixDisplay();
21065             msgEl.alignTo(f.el, 'tl-tr');
21066             msgEl.slideIn('l', {stopFx:true});
21067         },
21068
21069         hide : function(msgEl, f){
21070             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21071         }
21072     }
21073 };/*
21074  * Based on:
21075  * Ext JS Library 1.1.1
21076  * Copyright(c) 2006-2007, Ext JS, LLC.
21077  *
21078  * Originally Released Under LGPL - original licence link has changed is not relivant.
21079  *
21080  * Fork - LGPL
21081  * <script type="text/javascript">
21082  */
21083  
21084
21085 /**
21086  * @class Roo.form.TextField
21087  * @extends Roo.form.Field
21088  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21089  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21090  * @constructor
21091  * Creates a new TextField
21092  * @param {Object} config Configuration options
21093  */
21094 Roo.form.TextField = function(config){
21095     Roo.form.TextField.superclass.constructor.call(this, config);
21096     this.addEvents({
21097         /**
21098          * @event autosize
21099          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21100          * according to the default logic, but this event provides a hook for the developer to apply additional
21101          * logic at runtime to resize the field if needed.
21102              * @param {Roo.form.Field} this This text field
21103              * @param {Number} width The new field width
21104              */
21105         autosize : true
21106     });
21107 };
21108
21109 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21110     /**
21111      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21112      */
21113     grow : false,
21114     /**
21115      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21116      */
21117     growMin : 30,
21118     /**
21119      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21120      */
21121     growMax : 800,
21122     /**
21123      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21124      */
21125     vtype : null,
21126     /**
21127      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21128      */
21129     maskRe : null,
21130     /**
21131      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21132      */
21133     disableKeyFilter : false,
21134     /**
21135      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21136      */
21137     allowBlank : true,
21138     /**
21139      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21140      */
21141     minLength : 0,
21142     /**
21143      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21144      */
21145     maxLength : Number.MAX_VALUE,
21146     /**
21147      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21148      */
21149     minLengthText : "The minimum length for this field is {0}",
21150     /**
21151      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21152      */
21153     maxLengthText : "The maximum length for this field is {0}",
21154     /**
21155      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21156      */
21157     selectOnFocus : false,
21158     /**
21159      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21160      */
21161     blankText : "This field is required",
21162     /**
21163      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21164      * If available, this function will be called only after the basic validators all return true, and will be passed the
21165      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21166      */
21167     validator : null,
21168     /**
21169      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21170      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21171      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21172      */
21173     regex : null,
21174     /**
21175      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21176      */
21177     regexText : "",
21178     /**
21179      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21180      */
21181     emptyText : null,
21182    
21183
21184     // private
21185     initEvents : function()
21186     {
21187         if (this.emptyText) {
21188             this.el.attr('placeholder', this.emptyText);
21189         }
21190         
21191         Roo.form.TextField.superclass.initEvents.call(this);
21192         if(this.validationEvent == 'keyup'){
21193             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21194             this.el.on('keyup', this.filterValidation, this);
21195         }
21196         else if(this.validationEvent !== false){
21197             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21198         }
21199         
21200         if(this.selectOnFocus){
21201             this.on("focus", this.preFocus, this);
21202             
21203         }
21204         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21205             this.el.on("keypress", this.filterKeys, this);
21206         }
21207         if(this.grow){
21208             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21209             this.el.on("click", this.autoSize,  this);
21210         }
21211         if(this.el.is('input[type=password]') && Roo.isSafari){
21212             this.el.on('keydown', this.SafariOnKeyDown, this);
21213         }
21214     },
21215
21216     processValue : function(value){
21217         if(this.stripCharsRe){
21218             var newValue = value.replace(this.stripCharsRe, '');
21219             if(newValue !== value){
21220                 this.setRawValue(newValue);
21221                 return newValue;
21222             }
21223         }
21224         return value;
21225     },
21226
21227     filterValidation : function(e){
21228         if(!e.isNavKeyPress()){
21229             this.validationTask.delay(this.validationDelay);
21230         }
21231     },
21232
21233     // private
21234     onKeyUp : function(e){
21235         if(!e.isNavKeyPress()){
21236             this.autoSize();
21237         }
21238     },
21239
21240     /**
21241      * Resets the current field value to the originally-loaded value and clears any validation messages.
21242      *  
21243      */
21244     reset : function(){
21245         Roo.form.TextField.superclass.reset.call(this);
21246        
21247     },
21248
21249     
21250     // private
21251     preFocus : function(){
21252         
21253         if(this.selectOnFocus){
21254             this.el.dom.select();
21255         }
21256     },
21257
21258     
21259     // private
21260     filterKeys : function(e){
21261         var k = e.getKey();
21262         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21263             return;
21264         }
21265         var c = e.getCharCode(), cc = String.fromCharCode(c);
21266         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21267             return;
21268         }
21269         if(!this.maskRe.test(cc)){
21270             e.stopEvent();
21271         }
21272     },
21273
21274     setValue : function(v){
21275         
21276         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21277         
21278         this.autoSize();
21279     },
21280
21281     /**
21282      * Validates a value according to the field's validation rules and marks the field as invalid
21283      * if the validation fails
21284      * @param {Mixed} value The value to validate
21285      * @return {Boolean} True if the value is valid, else false
21286      */
21287     validateValue : function(value){
21288         if(value.length < 1)  { // if it's blank
21289              if(this.allowBlank){
21290                 this.clearInvalid();
21291                 return true;
21292              }else{
21293                 this.markInvalid(this.blankText);
21294                 return false;
21295              }
21296         }
21297         if(value.length < this.minLength){
21298             this.markInvalid(String.format(this.minLengthText, this.minLength));
21299             return false;
21300         }
21301         if(value.length > this.maxLength){
21302             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21303             return false;
21304         }
21305         if(this.vtype){
21306             var vt = Roo.form.VTypes;
21307             if(!vt[this.vtype](value, this)){
21308                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21309                 return false;
21310             }
21311         }
21312         if(typeof this.validator == "function"){
21313             var msg = this.validator(value);
21314             if(msg !== true){
21315                 this.markInvalid(msg);
21316                 return false;
21317             }
21318         }
21319         if(this.regex && !this.regex.test(value)){
21320             this.markInvalid(this.regexText);
21321             return false;
21322         }
21323         return true;
21324     },
21325
21326     /**
21327      * Selects text in this field
21328      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21329      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21330      */
21331     selectText : function(start, end){
21332         var v = this.getRawValue();
21333         if(v.length > 0){
21334             start = start === undefined ? 0 : start;
21335             end = end === undefined ? v.length : end;
21336             var d = this.el.dom;
21337             if(d.setSelectionRange){
21338                 d.setSelectionRange(start, end);
21339             }else if(d.createTextRange){
21340                 var range = d.createTextRange();
21341                 range.moveStart("character", start);
21342                 range.moveEnd("character", v.length-end);
21343                 range.select();
21344             }
21345         }
21346     },
21347
21348     /**
21349      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21350      * This only takes effect if grow = true, and fires the autosize event.
21351      */
21352     autoSize : function(){
21353         if(!this.grow || !this.rendered){
21354             return;
21355         }
21356         if(!this.metrics){
21357             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21358         }
21359         var el = this.el;
21360         var v = el.dom.value;
21361         var d = document.createElement('div');
21362         d.appendChild(document.createTextNode(v));
21363         v = d.innerHTML;
21364         d = null;
21365         v += "&#160;";
21366         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21367         this.el.setWidth(w);
21368         this.fireEvent("autosize", this, w);
21369     },
21370     
21371     // private
21372     SafariOnKeyDown : function(event)
21373     {
21374         // this is a workaround for a password hang bug on chrome/ webkit.
21375         
21376         var isSelectAll = false;
21377         
21378         if(this.el.dom.selectionEnd > 0){
21379             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21380         }
21381         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21382             event.preventDefault();
21383             this.setValue('');
21384             return;
21385         }
21386         
21387         if(isSelectAll){ // backspace and delete key
21388             
21389             event.preventDefault();
21390             // this is very hacky as keydown always get's upper case.
21391             //
21392             var cc = String.fromCharCode(event.getCharCode());
21393             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21394             
21395         }
21396         
21397         
21398     }
21399 });/*
21400  * Based on:
21401  * Ext JS Library 1.1.1
21402  * Copyright(c) 2006-2007, Ext JS, LLC.
21403  *
21404  * Originally Released Under LGPL - original licence link has changed is not relivant.
21405  *
21406  * Fork - LGPL
21407  * <script type="text/javascript">
21408  */
21409  
21410 /**
21411  * @class Roo.form.Hidden
21412  * @extends Roo.form.TextField
21413  * Simple Hidden element used on forms 
21414  * 
21415  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21416  * 
21417  * @constructor
21418  * Creates a new Hidden form element.
21419  * @param {Object} config Configuration options
21420  */
21421
21422
21423
21424 // easy hidden field...
21425 Roo.form.Hidden = function(config){
21426     Roo.form.Hidden.superclass.constructor.call(this, config);
21427 };
21428   
21429 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21430     fieldLabel:      '',
21431     inputType:      'hidden',
21432     width:          50,
21433     allowBlank:     true,
21434     labelSeparator: '',
21435     hidden:         true,
21436     itemCls :       'x-form-item-display-none'
21437
21438
21439 });
21440
21441
21442 /*
21443  * Based on:
21444  * Ext JS Library 1.1.1
21445  * Copyright(c) 2006-2007, Ext JS, LLC.
21446  *
21447  * Originally Released Under LGPL - original licence link has changed is not relivant.
21448  *
21449  * Fork - LGPL
21450  * <script type="text/javascript">
21451  */
21452  
21453 /**
21454  * @class Roo.form.TriggerField
21455  * @extends Roo.form.TextField
21456  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21457  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21458  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21459  * for which you can provide a custom implementation.  For example:
21460  * <pre><code>
21461 var trigger = new Roo.form.TriggerField();
21462 trigger.onTriggerClick = myTriggerFn;
21463 trigger.applyTo('my-field');
21464 </code></pre>
21465  *
21466  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21467  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21468  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21469  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21470  * @constructor
21471  * Create a new TriggerField.
21472  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21473  * to the base TextField)
21474  */
21475 Roo.form.TriggerField = function(config){
21476     this.mimicing = false;
21477     Roo.form.TriggerField.superclass.constructor.call(this, config);
21478 };
21479
21480 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21481     /**
21482      * @cfg {String} triggerClass A CSS class to apply to the trigger
21483      */
21484     /**
21485      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21486      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21487      */
21488     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21489     /**
21490      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21491      */
21492     hideTrigger:false,
21493
21494     /** @cfg {Boolean} grow @hide */
21495     /** @cfg {Number} growMin @hide */
21496     /** @cfg {Number} growMax @hide */
21497
21498     /**
21499      * @hide 
21500      * @method
21501      */
21502     autoSize: Roo.emptyFn,
21503     // private
21504     monitorTab : true,
21505     // private
21506     deferHeight : true,
21507
21508     
21509     actionMode : 'wrap',
21510     // private
21511     onResize : function(w, h){
21512         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21513         if(typeof w == 'number'){
21514             var x = w - this.trigger.getWidth();
21515             this.el.setWidth(this.adjustWidth('input', x));
21516             this.trigger.setStyle('left', x+'px');
21517         }
21518     },
21519
21520     // private
21521     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21522
21523     // private
21524     getResizeEl : function(){
21525         return this.wrap;
21526     },
21527
21528     // private
21529     getPositionEl : function(){
21530         return this.wrap;
21531     },
21532
21533     // private
21534     alignErrorIcon : function(){
21535         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21536     },
21537
21538     // private
21539     onRender : function(ct, position){
21540         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21541         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21542         this.trigger = this.wrap.createChild(this.triggerConfig ||
21543                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21544         if(this.hideTrigger){
21545             this.trigger.setDisplayed(false);
21546         }
21547         this.initTrigger();
21548         if(!this.width){
21549             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21550         }
21551     },
21552
21553     // private
21554     initTrigger : function(){
21555         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21556         this.trigger.addClassOnOver('x-form-trigger-over');
21557         this.trigger.addClassOnClick('x-form-trigger-click');
21558     },
21559
21560     // private
21561     onDestroy : function(){
21562         if(this.trigger){
21563             this.trigger.removeAllListeners();
21564             this.trigger.remove();
21565         }
21566         if(this.wrap){
21567             this.wrap.remove();
21568         }
21569         Roo.form.TriggerField.superclass.onDestroy.call(this);
21570     },
21571
21572     // private
21573     onFocus : function(){
21574         Roo.form.TriggerField.superclass.onFocus.call(this);
21575         if(!this.mimicing){
21576             this.wrap.addClass('x-trigger-wrap-focus');
21577             this.mimicing = true;
21578             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21579             if(this.monitorTab){
21580                 this.el.on("keydown", this.checkTab, this);
21581             }
21582         }
21583     },
21584
21585     // private
21586     checkTab : function(e){
21587         if(e.getKey() == e.TAB){
21588             this.triggerBlur();
21589         }
21590     },
21591
21592     // private
21593     onBlur : function(){
21594         // do nothing
21595     },
21596
21597     // private
21598     mimicBlur : function(e, t){
21599         if(!this.wrap.contains(t) && this.validateBlur()){
21600             this.triggerBlur();
21601         }
21602     },
21603
21604     // private
21605     triggerBlur : function(){
21606         this.mimicing = false;
21607         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21608         if(this.monitorTab){
21609             this.el.un("keydown", this.checkTab, this);
21610         }
21611         this.wrap.removeClass('x-trigger-wrap-focus');
21612         Roo.form.TriggerField.superclass.onBlur.call(this);
21613     },
21614
21615     // private
21616     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21617     validateBlur : function(e, t){
21618         return true;
21619     },
21620
21621     // private
21622     onDisable : function(){
21623         Roo.form.TriggerField.superclass.onDisable.call(this);
21624         if(this.wrap){
21625             this.wrap.addClass('x-item-disabled');
21626         }
21627     },
21628
21629     // private
21630     onEnable : function(){
21631         Roo.form.TriggerField.superclass.onEnable.call(this);
21632         if(this.wrap){
21633             this.wrap.removeClass('x-item-disabled');
21634         }
21635     },
21636
21637     // private
21638     onShow : function(){
21639         var ae = this.getActionEl();
21640         
21641         if(ae){
21642             ae.dom.style.display = '';
21643             ae.dom.style.visibility = 'visible';
21644         }
21645     },
21646
21647     // private
21648     
21649     onHide : function(){
21650         var ae = this.getActionEl();
21651         ae.dom.style.display = 'none';
21652     },
21653
21654     /**
21655      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21656      * by an implementing function.
21657      * @method
21658      * @param {EventObject} e
21659      */
21660     onTriggerClick : Roo.emptyFn
21661 });
21662
21663 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21664 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21665 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21666 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21667     initComponent : function(){
21668         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21669
21670         this.triggerConfig = {
21671             tag:'span', cls:'x-form-twin-triggers', cn:[
21672             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21673             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21674         ]};
21675     },
21676
21677     getTrigger : function(index){
21678         return this.triggers[index];
21679     },
21680
21681     initTrigger : function(){
21682         var ts = this.trigger.select('.x-form-trigger', true);
21683         this.wrap.setStyle('overflow', 'hidden');
21684         var triggerField = this;
21685         ts.each(function(t, all, index){
21686             t.hide = function(){
21687                 var w = triggerField.wrap.getWidth();
21688                 this.dom.style.display = 'none';
21689                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21690             };
21691             t.show = function(){
21692                 var w = triggerField.wrap.getWidth();
21693                 this.dom.style.display = '';
21694                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21695             };
21696             var triggerIndex = 'Trigger'+(index+1);
21697
21698             if(this['hide'+triggerIndex]){
21699                 t.dom.style.display = 'none';
21700             }
21701             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21702             t.addClassOnOver('x-form-trigger-over');
21703             t.addClassOnClick('x-form-trigger-click');
21704         }, this);
21705         this.triggers = ts.elements;
21706     },
21707
21708     onTrigger1Click : Roo.emptyFn,
21709     onTrigger2Click : Roo.emptyFn
21710 });/*
21711  * Based on:
21712  * Ext JS Library 1.1.1
21713  * Copyright(c) 2006-2007, Ext JS, LLC.
21714  *
21715  * Originally Released Under LGPL - original licence link has changed is not relivant.
21716  *
21717  * Fork - LGPL
21718  * <script type="text/javascript">
21719  */
21720  
21721 /**
21722  * @class Roo.form.TextArea
21723  * @extends Roo.form.TextField
21724  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21725  * support for auto-sizing.
21726  * @constructor
21727  * Creates a new TextArea
21728  * @param {Object} config Configuration options
21729  */
21730 Roo.form.TextArea = function(config){
21731     Roo.form.TextArea.superclass.constructor.call(this, config);
21732     // these are provided exchanges for backwards compat
21733     // minHeight/maxHeight were replaced by growMin/growMax to be
21734     // compatible with TextField growing config values
21735     if(this.minHeight !== undefined){
21736         this.growMin = this.minHeight;
21737     }
21738     if(this.maxHeight !== undefined){
21739         this.growMax = this.maxHeight;
21740     }
21741 };
21742
21743 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21744     /**
21745      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21746      */
21747     growMin : 60,
21748     /**
21749      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21750      */
21751     growMax: 1000,
21752     /**
21753      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21754      * in the field (equivalent to setting overflow: hidden, defaults to false)
21755      */
21756     preventScrollbars: false,
21757     /**
21758      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21759      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21760      */
21761
21762     // private
21763     onRender : function(ct, position){
21764         if(!this.el){
21765             this.defaultAutoCreate = {
21766                 tag: "textarea",
21767                 style:"width:300px;height:60px;",
21768                 autocomplete: "off"
21769             };
21770         }
21771         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21772         if(this.grow){
21773             this.textSizeEl = Roo.DomHelper.append(document.body, {
21774                 tag: "pre", cls: "x-form-grow-sizer"
21775             });
21776             if(this.preventScrollbars){
21777                 this.el.setStyle("overflow", "hidden");
21778             }
21779             this.el.setHeight(this.growMin);
21780         }
21781     },
21782
21783     onDestroy : function(){
21784         if(this.textSizeEl){
21785             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21786         }
21787         Roo.form.TextArea.superclass.onDestroy.call(this);
21788     },
21789
21790     // private
21791     onKeyUp : function(e){
21792         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21793             this.autoSize();
21794         }
21795     },
21796
21797     /**
21798      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21799      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21800      */
21801     autoSize : function(){
21802         if(!this.grow || !this.textSizeEl){
21803             return;
21804         }
21805         var el = this.el;
21806         var v = el.dom.value;
21807         var ts = this.textSizeEl;
21808
21809         ts.innerHTML = '';
21810         ts.appendChild(document.createTextNode(v));
21811         v = ts.innerHTML;
21812
21813         Roo.fly(ts).setWidth(this.el.getWidth());
21814         if(v.length < 1){
21815             v = "&#160;&#160;";
21816         }else{
21817             if(Roo.isIE){
21818                 v = v.replace(/\n/g, '<p>&#160;</p>');
21819             }
21820             v += "&#160;\n&#160;";
21821         }
21822         ts.innerHTML = v;
21823         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21824         if(h != this.lastHeight){
21825             this.lastHeight = h;
21826             this.el.setHeight(h);
21827             this.fireEvent("autosize", this, h);
21828         }
21829     }
21830 });/*
21831  * Based on:
21832  * Ext JS Library 1.1.1
21833  * Copyright(c) 2006-2007, Ext JS, LLC.
21834  *
21835  * Originally Released Under LGPL - original licence link has changed is not relivant.
21836  *
21837  * Fork - LGPL
21838  * <script type="text/javascript">
21839  */
21840  
21841
21842 /**
21843  * @class Roo.form.NumberField
21844  * @extends Roo.form.TextField
21845  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21846  * @constructor
21847  * Creates a new NumberField
21848  * @param {Object} config Configuration options
21849  */
21850 Roo.form.NumberField = function(config){
21851     Roo.form.NumberField.superclass.constructor.call(this, config);
21852 };
21853
21854 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21855     /**
21856      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21857      */
21858     fieldClass: "x-form-field x-form-num-field",
21859     /**
21860      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21861      */
21862     allowDecimals : true,
21863     /**
21864      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21865      */
21866     decimalSeparator : ".",
21867     /**
21868      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21869      */
21870     decimalPrecision : 2,
21871     /**
21872      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21873      */
21874     allowNegative : true,
21875     /**
21876      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21877      */
21878     minValue : Number.NEGATIVE_INFINITY,
21879     /**
21880      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21881      */
21882     maxValue : Number.MAX_VALUE,
21883     /**
21884      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21885      */
21886     minText : "The minimum value for this field is {0}",
21887     /**
21888      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21889      */
21890     maxText : "The maximum value for this field is {0}",
21891     /**
21892      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21893      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21894      */
21895     nanText : "{0} is not a valid number",
21896
21897     // private
21898     initEvents : function(){
21899         Roo.form.NumberField.superclass.initEvents.call(this);
21900         var allowed = "0123456789";
21901         if(this.allowDecimals){
21902             allowed += this.decimalSeparator;
21903         }
21904         if(this.allowNegative){
21905             allowed += "-";
21906         }
21907         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21908         var keyPress = function(e){
21909             var k = e.getKey();
21910             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21911                 return;
21912             }
21913             var c = e.getCharCode();
21914             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21915                 e.stopEvent();
21916             }
21917         };
21918         this.el.on("keypress", keyPress, this);
21919     },
21920
21921     // private
21922     validateValue : function(value){
21923         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21924             return false;
21925         }
21926         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21927              return true;
21928         }
21929         var num = this.parseValue(value);
21930         if(isNaN(num)){
21931             this.markInvalid(String.format(this.nanText, value));
21932             return false;
21933         }
21934         if(num < this.minValue){
21935             this.markInvalid(String.format(this.minText, this.minValue));
21936             return false;
21937         }
21938         if(num > this.maxValue){
21939             this.markInvalid(String.format(this.maxText, this.maxValue));
21940             return false;
21941         }
21942         return true;
21943     },
21944
21945     getValue : function(){
21946         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
21947     },
21948
21949     // private
21950     parseValue : function(value){
21951         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
21952         return isNaN(value) ? '' : value;
21953     },
21954
21955     // private
21956     fixPrecision : function(value){
21957         var nan = isNaN(value);
21958         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
21959             return nan ? '' : value;
21960         }
21961         return parseFloat(value).toFixed(this.decimalPrecision);
21962     },
21963
21964     setValue : function(v){
21965         v = this.fixPrecision(v);
21966         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
21967     },
21968
21969     // private
21970     decimalPrecisionFcn : function(v){
21971         return Math.floor(v);
21972     },
21973
21974     beforeBlur : function(){
21975         var v = this.parseValue(this.getRawValue());
21976         if(v){
21977             this.setValue(v);
21978         }
21979     }
21980 });/*
21981  * Based on:
21982  * Ext JS Library 1.1.1
21983  * Copyright(c) 2006-2007, Ext JS, LLC.
21984  *
21985  * Originally Released Under LGPL - original licence link has changed is not relivant.
21986  *
21987  * Fork - LGPL
21988  * <script type="text/javascript">
21989  */
21990  
21991 /**
21992  * @class Roo.form.DateField
21993  * @extends Roo.form.TriggerField
21994  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
21995 * @constructor
21996 * Create a new DateField
21997 * @param {Object} config
21998  */
21999 Roo.form.DateField = function(config){
22000     Roo.form.DateField.superclass.constructor.call(this, config);
22001     
22002       this.addEvents({
22003          
22004         /**
22005          * @event select
22006          * Fires when a date is selected
22007              * @param {Roo.form.DateField} combo This combo box
22008              * @param {Date} date The date selected
22009              */
22010         'select' : true
22011          
22012     });
22013     
22014     
22015     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22016     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22017     this.ddMatch = null;
22018     if(this.disabledDates){
22019         var dd = this.disabledDates;
22020         var re = "(?:";
22021         for(var i = 0; i < dd.length; i++){
22022             re += dd[i];
22023             if(i != dd.length-1) re += "|";
22024         }
22025         this.ddMatch = new RegExp(re + ")");
22026     }
22027 };
22028
22029 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22030     /**
22031      * @cfg {String} format
22032      * The default date format string which can be overriden for localization support.  The format must be
22033      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22034      */
22035     format : "m/d/y",
22036     /**
22037      * @cfg {String} altFormats
22038      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22039      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22040      */
22041     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22042     /**
22043      * @cfg {Array} disabledDays
22044      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22045      */
22046     disabledDays : null,
22047     /**
22048      * @cfg {String} disabledDaysText
22049      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22050      */
22051     disabledDaysText : "Disabled",
22052     /**
22053      * @cfg {Array} disabledDates
22054      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22055      * expression so they are very powerful. Some examples:
22056      * <ul>
22057      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22058      * <li>["03/08", "09/16"] would disable those days for every year</li>
22059      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22060      * <li>["03/../2006"] would disable every day in March 2006</li>
22061      * <li>["^03"] would disable every day in every March</li>
22062      * </ul>
22063      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22064      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22065      */
22066     disabledDates : null,
22067     /**
22068      * @cfg {String} disabledDatesText
22069      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22070      */
22071     disabledDatesText : "Disabled",
22072     /**
22073      * @cfg {Date/String} minValue
22074      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22075      * valid format (defaults to null).
22076      */
22077     minValue : null,
22078     /**
22079      * @cfg {Date/String} maxValue
22080      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22081      * valid format (defaults to null).
22082      */
22083     maxValue : null,
22084     /**
22085      * @cfg {String} minText
22086      * The error text to display when the date in the cell is before minValue (defaults to
22087      * 'The date in this field must be after {minValue}').
22088      */
22089     minText : "The date in this field must be equal to or after {0}",
22090     /**
22091      * @cfg {String} maxText
22092      * The error text to display when the date in the cell is after maxValue (defaults to
22093      * 'The date in this field must be before {maxValue}').
22094      */
22095     maxText : "The date in this field must be equal to or before {0}",
22096     /**
22097      * @cfg {String} invalidText
22098      * The error text to display when the date in the field is invalid (defaults to
22099      * '{value} is not a valid date - it must be in the format {format}').
22100      */
22101     invalidText : "{0} is not a valid date - it must be in the format {1}",
22102     /**
22103      * @cfg {String} triggerClass
22104      * An additional CSS class used to style the trigger button.  The trigger will always get the
22105      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22106      * which displays a calendar icon).
22107      */
22108     triggerClass : 'x-form-date-trigger',
22109     
22110
22111     /**
22112      * @cfg {Boolean} useIso
22113      * if enabled, then the date field will use a hidden field to store the 
22114      * real value as iso formated date. default (false)
22115      */ 
22116     useIso : false,
22117     /**
22118      * @cfg {String/Object} autoCreate
22119      * A DomHelper element spec, or true for a default element spec (defaults to
22120      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22121      */ 
22122     // private
22123     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22124     
22125     // private
22126     hiddenField: false,
22127     
22128     onRender : function(ct, position)
22129     {
22130         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22131         if (this.useIso) {
22132             //this.el.dom.removeAttribute('name'); 
22133             Roo.log("Changing name?");
22134             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22135             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22136                     'before', true);
22137             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22138             // prevent input submission
22139             this.hiddenName = this.name;
22140         }
22141             
22142             
22143     },
22144     
22145     // private
22146     validateValue : function(value)
22147     {
22148         value = this.formatDate(value);
22149         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22150             Roo.log('super failed');
22151             return false;
22152         }
22153         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22154              return true;
22155         }
22156         var svalue = value;
22157         value = this.parseDate(value);
22158         if(!value){
22159             Roo.log('parse date failed' + svalue);
22160             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22161             return false;
22162         }
22163         var time = value.getTime();
22164         if(this.minValue && time < this.minValue.getTime()){
22165             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22166             return false;
22167         }
22168         if(this.maxValue && time > this.maxValue.getTime()){
22169             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22170             return false;
22171         }
22172         if(this.disabledDays){
22173             var day = value.getDay();
22174             for(var i = 0; i < this.disabledDays.length; i++) {
22175                 if(day === this.disabledDays[i]){
22176                     this.markInvalid(this.disabledDaysText);
22177                     return false;
22178                 }
22179             }
22180         }
22181         var fvalue = this.formatDate(value);
22182         if(this.ddMatch && this.ddMatch.test(fvalue)){
22183             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22184             return false;
22185         }
22186         return true;
22187     },
22188
22189     // private
22190     // Provides logic to override the default TriggerField.validateBlur which just returns true
22191     validateBlur : function(){
22192         return !this.menu || !this.menu.isVisible();
22193     },
22194     
22195     getName: function()
22196     {
22197         // returns hidden if it's set..
22198         if (!this.rendered) {return ''};
22199         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22200         
22201     },
22202
22203     /**
22204      * Returns the current date value of the date field.
22205      * @return {Date} The date value
22206      */
22207     getValue : function(){
22208         
22209         return  this.hiddenField ?
22210                 this.hiddenField.value :
22211                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22212     },
22213
22214     /**
22215      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22216      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22217      * (the default format used is "m/d/y").
22218      * <br />Usage:
22219      * <pre><code>
22220 //All of these calls set the same date value (May 4, 2006)
22221
22222 //Pass a date object:
22223 var dt = new Date('5/4/06');
22224 dateField.setValue(dt);
22225
22226 //Pass a date string (default format):
22227 dateField.setValue('5/4/06');
22228
22229 //Pass a date string (custom format):
22230 dateField.format = 'Y-m-d';
22231 dateField.setValue('2006-5-4');
22232 </code></pre>
22233      * @param {String/Date} date The date or valid date string
22234      */
22235     setValue : function(date){
22236         if (this.hiddenField) {
22237             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22238         }
22239         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22240         // make sure the value field is always stored as a date..
22241         this.value = this.parseDate(date);
22242         
22243         
22244     },
22245
22246     // private
22247     parseDate : function(value){
22248         if(!value || value instanceof Date){
22249             return value;
22250         }
22251         var v = Date.parseDate(value, this.format);
22252          if (!v && this.useIso) {
22253             v = Date.parseDate(value, 'Y-m-d');
22254         }
22255         if(!v && this.altFormats){
22256             if(!this.altFormatsArray){
22257                 this.altFormatsArray = this.altFormats.split("|");
22258             }
22259             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22260                 v = Date.parseDate(value, this.altFormatsArray[i]);
22261             }
22262         }
22263         return v;
22264     },
22265
22266     // private
22267     formatDate : function(date, fmt){
22268         return (!date || !(date instanceof Date)) ?
22269                date : date.dateFormat(fmt || this.format);
22270     },
22271
22272     // private
22273     menuListeners : {
22274         select: function(m, d){
22275             
22276             this.setValue(d);
22277             this.fireEvent('select', this, d);
22278         },
22279         show : function(){ // retain focus styling
22280             this.onFocus();
22281         },
22282         hide : function(){
22283             this.focus.defer(10, this);
22284             var ml = this.menuListeners;
22285             this.menu.un("select", ml.select,  this);
22286             this.menu.un("show", ml.show,  this);
22287             this.menu.un("hide", ml.hide,  this);
22288         }
22289     },
22290
22291     // private
22292     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22293     onTriggerClick : function(){
22294         if(this.disabled){
22295             return;
22296         }
22297         if(this.menu == null){
22298             this.menu = new Roo.menu.DateMenu();
22299         }
22300         Roo.apply(this.menu.picker,  {
22301             showClear: this.allowBlank,
22302             minDate : this.minValue,
22303             maxDate : this.maxValue,
22304             disabledDatesRE : this.ddMatch,
22305             disabledDatesText : this.disabledDatesText,
22306             disabledDays : this.disabledDays,
22307             disabledDaysText : this.disabledDaysText,
22308             format : this.useIso ? 'Y-m-d' : this.format,
22309             minText : String.format(this.minText, this.formatDate(this.minValue)),
22310             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22311         });
22312         this.menu.on(Roo.apply({}, this.menuListeners, {
22313             scope:this
22314         }));
22315         this.menu.picker.setValue(this.getValue() || new Date());
22316         this.menu.show(this.el, "tl-bl?");
22317     },
22318
22319     beforeBlur : function(){
22320         var v = this.parseDate(this.getRawValue());
22321         if(v){
22322             this.setValue(v);
22323         }
22324     },
22325
22326     /*@
22327      * overide
22328      * 
22329      */
22330     isDirty : function() {
22331         if(this.disabled) {
22332             return false;
22333         }
22334         
22335         if(typeof(this.startValue) === 'undefined'){
22336             return false;
22337         }
22338         
22339         return String(this.getValue()) !== String(this.startValue);
22340         
22341     }
22342 });/*
22343  * Based on:
22344  * Ext JS Library 1.1.1
22345  * Copyright(c) 2006-2007, Ext JS, LLC.
22346  *
22347  * Originally Released Under LGPL - original licence link has changed is not relivant.
22348  *
22349  * Fork - LGPL
22350  * <script type="text/javascript">
22351  */
22352  
22353 /**
22354  * @class Roo.form.MonthField
22355  * @extends Roo.form.TriggerField
22356  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22357 * @constructor
22358 * Create a new MonthField
22359 * @param {Object} config
22360  */
22361 Roo.form.MonthField = function(config){
22362     
22363     Roo.form.MonthField.superclass.constructor.call(this, config);
22364     
22365       this.addEvents({
22366          
22367         /**
22368          * @event select
22369          * Fires when a date is selected
22370              * @param {Roo.form.MonthFieeld} combo This combo box
22371              * @param {Date} date The date selected
22372              */
22373         'select' : true
22374          
22375     });
22376     
22377     
22378     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22379     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22380     this.ddMatch = null;
22381     if(this.disabledDates){
22382         var dd = this.disabledDates;
22383         var re = "(?:";
22384         for(var i = 0; i < dd.length; i++){
22385             re += dd[i];
22386             if(i != dd.length-1) re += "|";
22387         }
22388         this.ddMatch = new RegExp(re + ")");
22389     }
22390 };
22391
22392 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22393     /**
22394      * @cfg {String} format
22395      * The default date format string which can be overriden for localization support.  The format must be
22396      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22397      */
22398     format : "M Y",
22399     /**
22400      * @cfg {String} altFormats
22401      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22402      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22403      */
22404     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22405     /**
22406      * @cfg {Array} disabledDays
22407      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22408      */
22409     disabledDays : [0,1,2,3,4,5,6],
22410     /**
22411      * @cfg {String} disabledDaysText
22412      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22413      */
22414     disabledDaysText : "Disabled",
22415     /**
22416      * @cfg {Array} disabledDates
22417      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22418      * expression so they are very powerful. Some examples:
22419      * <ul>
22420      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22421      * <li>["03/08", "09/16"] would disable those days for every year</li>
22422      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22423      * <li>["03/../2006"] would disable every day in March 2006</li>
22424      * <li>["^03"] would disable every day in every March</li>
22425      * </ul>
22426      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22427      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22428      */
22429     disabledDates : null,
22430     /**
22431      * @cfg {String} disabledDatesText
22432      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22433      */
22434     disabledDatesText : "Disabled",
22435     /**
22436      * @cfg {Date/String} minValue
22437      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22438      * valid format (defaults to null).
22439      */
22440     minValue : null,
22441     /**
22442      * @cfg {Date/String} maxValue
22443      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22444      * valid format (defaults to null).
22445      */
22446     maxValue : null,
22447     /**
22448      * @cfg {String} minText
22449      * The error text to display when the date in the cell is before minValue (defaults to
22450      * 'The date in this field must be after {minValue}').
22451      */
22452     minText : "The date in this field must be equal to or after {0}",
22453     /**
22454      * @cfg {String} maxTextf
22455      * The error text to display when the date in the cell is after maxValue (defaults to
22456      * 'The date in this field must be before {maxValue}').
22457      */
22458     maxText : "The date in this field must be equal to or before {0}",
22459     /**
22460      * @cfg {String} invalidText
22461      * The error text to display when the date in the field is invalid (defaults to
22462      * '{value} is not a valid date - it must be in the format {format}').
22463      */
22464     invalidText : "{0} is not a valid date - it must be in the format {1}",
22465     /**
22466      * @cfg {String} triggerClass
22467      * An additional CSS class used to style the trigger button.  The trigger will always get the
22468      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22469      * which displays a calendar icon).
22470      */
22471     triggerClass : 'x-form-date-trigger',
22472     
22473
22474     /**
22475      * @cfg {Boolean} useIso
22476      * if enabled, then the date field will use a hidden field to store the 
22477      * real value as iso formated date. default (true)
22478      */ 
22479     useIso : true,
22480     /**
22481      * @cfg {String/Object} autoCreate
22482      * A DomHelper element spec, or true for a default element spec (defaults to
22483      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22484      */ 
22485     // private
22486     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22487     
22488     // private
22489     hiddenField: false,
22490     
22491     hideMonthPicker : false,
22492     
22493     onRender : function(ct, position)
22494     {
22495         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22496         if (this.useIso) {
22497             this.el.dom.removeAttribute('name'); 
22498             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22499                     'before', true);
22500             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22501             // prevent input submission
22502             this.hiddenName = this.name;
22503         }
22504             
22505             
22506     },
22507     
22508     // private
22509     validateValue : function(value)
22510     {
22511         value = this.formatDate(value);
22512         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22513             return false;
22514         }
22515         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22516              return true;
22517         }
22518         var svalue = value;
22519         value = this.parseDate(value);
22520         if(!value){
22521             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22522             return false;
22523         }
22524         var time = value.getTime();
22525         if(this.minValue && time < this.minValue.getTime()){
22526             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22527             return false;
22528         }
22529         if(this.maxValue && time > this.maxValue.getTime()){
22530             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22531             return false;
22532         }
22533         /*if(this.disabledDays){
22534             var day = value.getDay();
22535             for(var i = 0; i < this.disabledDays.length; i++) {
22536                 if(day === this.disabledDays[i]){
22537                     this.markInvalid(this.disabledDaysText);
22538                     return false;
22539                 }
22540             }
22541         }
22542         */
22543         var fvalue = this.formatDate(value);
22544         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22545             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22546             return false;
22547         }
22548         */
22549         return true;
22550     },
22551
22552     // private
22553     // Provides logic to override the default TriggerField.validateBlur which just returns true
22554     validateBlur : function(){
22555         return !this.menu || !this.menu.isVisible();
22556     },
22557
22558     /**
22559      * Returns the current date value of the date field.
22560      * @return {Date} The date value
22561      */
22562     getValue : function(){
22563         
22564         
22565         
22566         return  this.hiddenField ?
22567                 this.hiddenField.value :
22568                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22569     },
22570
22571     /**
22572      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22573      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22574      * (the default format used is "m/d/y").
22575      * <br />Usage:
22576      * <pre><code>
22577 //All of these calls set the same date value (May 4, 2006)
22578
22579 //Pass a date object:
22580 var dt = new Date('5/4/06');
22581 monthField.setValue(dt);
22582
22583 //Pass a date string (default format):
22584 monthField.setValue('5/4/06');
22585
22586 //Pass a date string (custom format):
22587 monthField.format = 'Y-m-d';
22588 monthField.setValue('2006-5-4');
22589 </code></pre>
22590      * @param {String/Date} date The date or valid date string
22591      */
22592     setValue : function(date){
22593         Roo.log('month setValue' + date);
22594         // can only be first of month..
22595         
22596         var val = this.parseDate(date);
22597         
22598         if (this.hiddenField) {
22599             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22600         }
22601         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22602         this.value = this.parseDate(date);
22603     },
22604
22605     // private
22606     parseDate : function(value){
22607         if(!value || value instanceof Date){
22608             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22609             return value;
22610         }
22611         var v = Date.parseDate(value, this.format);
22612         if (!v && this.useIso) {
22613             v = Date.parseDate(value, 'Y-m-d');
22614         }
22615         if (v) {
22616             // 
22617             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22618         }
22619         
22620         
22621         if(!v && this.altFormats){
22622             if(!this.altFormatsArray){
22623                 this.altFormatsArray = this.altFormats.split("|");
22624             }
22625             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22626                 v = Date.parseDate(value, this.altFormatsArray[i]);
22627             }
22628         }
22629         return v;
22630     },
22631
22632     // private
22633     formatDate : function(date, fmt){
22634         return (!date || !(date instanceof Date)) ?
22635                date : date.dateFormat(fmt || this.format);
22636     },
22637
22638     // private
22639     menuListeners : {
22640         select: function(m, d){
22641             this.setValue(d);
22642             this.fireEvent('select', this, d);
22643         },
22644         show : function(){ // retain focus styling
22645             this.onFocus();
22646         },
22647         hide : function(){
22648             this.focus.defer(10, this);
22649             var ml = this.menuListeners;
22650             this.menu.un("select", ml.select,  this);
22651             this.menu.un("show", ml.show,  this);
22652             this.menu.un("hide", ml.hide,  this);
22653         }
22654     },
22655     // private
22656     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22657     onTriggerClick : function(){
22658         if(this.disabled){
22659             return;
22660         }
22661         if(this.menu == null){
22662             this.menu = new Roo.menu.DateMenu();
22663            
22664         }
22665         
22666         Roo.apply(this.menu.picker,  {
22667             
22668             showClear: this.allowBlank,
22669             minDate : this.minValue,
22670             maxDate : this.maxValue,
22671             disabledDatesRE : this.ddMatch,
22672             disabledDatesText : this.disabledDatesText,
22673             
22674             format : this.useIso ? 'Y-m-d' : this.format,
22675             minText : String.format(this.minText, this.formatDate(this.minValue)),
22676             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22677             
22678         });
22679          this.menu.on(Roo.apply({}, this.menuListeners, {
22680             scope:this
22681         }));
22682        
22683         
22684         var m = this.menu;
22685         var p = m.picker;
22686         
22687         // hide month picker get's called when we called by 'before hide';
22688         
22689         var ignorehide = true;
22690         p.hideMonthPicker  = function(disableAnim){
22691             if (ignorehide) {
22692                 return;
22693             }
22694              if(this.monthPicker){
22695                 Roo.log("hideMonthPicker called");
22696                 if(disableAnim === true){
22697                     this.monthPicker.hide();
22698                 }else{
22699                     this.monthPicker.slideOut('t', {duration:.2});
22700                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22701                     p.fireEvent("select", this, this.value);
22702                     m.hide();
22703                 }
22704             }
22705         }
22706         
22707         Roo.log('picker set value');
22708         Roo.log(this.getValue());
22709         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22710         m.show(this.el, 'tl-bl?');
22711         ignorehide  = false;
22712         // this will trigger hideMonthPicker..
22713         
22714         
22715         // hidden the day picker
22716         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22717         
22718         
22719         
22720       
22721         
22722         p.showMonthPicker.defer(100, p);
22723     
22724         
22725        
22726     },
22727
22728     beforeBlur : function(){
22729         var v = this.parseDate(this.getRawValue());
22730         if(v){
22731             this.setValue(v);
22732         }
22733     }
22734
22735     /** @cfg {Boolean} grow @hide */
22736     /** @cfg {Number} growMin @hide */
22737     /** @cfg {Number} growMax @hide */
22738     /**
22739      * @hide
22740      * @method autoSize
22741      */
22742 });/*
22743  * Based on:
22744  * Ext JS Library 1.1.1
22745  * Copyright(c) 2006-2007, Ext JS, LLC.
22746  *
22747  * Originally Released Under LGPL - original licence link has changed is not relivant.
22748  *
22749  * Fork - LGPL
22750  * <script type="text/javascript">
22751  */
22752  
22753
22754 /**
22755  * @class Roo.form.ComboBox
22756  * @extends Roo.form.TriggerField
22757  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22758  * @constructor
22759  * Create a new ComboBox.
22760  * @param {Object} config Configuration options
22761  */
22762 Roo.form.ComboBox = function(config){
22763     Roo.form.ComboBox.superclass.constructor.call(this, config);
22764     this.addEvents({
22765         /**
22766          * @event expand
22767          * Fires when the dropdown list is expanded
22768              * @param {Roo.form.ComboBox} combo This combo box
22769              */
22770         'expand' : true,
22771         /**
22772          * @event collapse
22773          * Fires when the dropdown list is collapsed
22774              * @param {Roo.form.ComboBox} combo This combo box
22775              */
22776         'collapse' : true,
22777         /**
22778          * @event beforeselect
22779          * Fires before a list item is selected. Return false to cancel the selection.
22780              * @param {Roo.form.ComboBox} combo This combo box
22781              * @param {Roo.data.Record} record The data record returned from the underlying store
22782              * @param {Number} index The index of the selected item in the dropdown list
22783              */
22784         'beforeselect' : true,
22785         /**
22786          * @event select
22787          * Fires when a list item is selected
22788              * @param {Roo.form.ComboBox} combo This combo box
22789              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22790              * @param {Number} index The index of the selected item in the dropdown list
22791              */
22792         'select' : true,
22793         /**
22794          * @event beforequery
22795          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22796          * The event object passed has these properties:
22797              * @param {Roo.form.ComboBox} combo This combo box
22798              * @param {String} query The query
22799              * @param {Boolean} forceAll true to force "all" query
22800              * @param {Boolean} cancel true to cancel the query
22801              * @param {Object} e The query event object
22802              */
22803         'beforequery': true,
22804          /**
22805          * @event add
22806          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22807              * @param {Roo.form.ComboBox} combo This combo box
22808              */
22809         'add' : true,
22810         /**
22811          * @event edit
22812          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22813              * @param {Roo.form.ComboBox} combo This combo box
22814              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22815              */
22816         'edit' : true
22817         
22818         
22819     });
22820     if(this.transform){
22821         this.allowDomMove = false;
22822         var s = Roo.getDom(this.transform);
22823         if(!this.hiddenName){
22824             this.hiddenName = s.name;
22825         }
22826         if(!this.store){
22827             this.mode = 'local';
22828             var d = [], opts = s.options;
22829             for(var i = 0, len = opts.length;i < len; i++){
22830                 var o = opts[i];
22831                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22832                 if(o.selected) {
22833                     this.value = value;
22834                 }
22835                 d.push([value, o.text]);
22836             }
22837             this.store = new Roo.data.SimpleStore({
22838                 'id': 0,
22839                 fields: ['value', 'text'],
22840                 data : d
22841             });
22842             this.valueField = 'value';
22843             this.displayField = 'text';
22844         }
22845         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22846         if(!this.lazyRender){
22847             this.target = true;
22848             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22849             s.parentNode.removeChild(s); // remove it
22850             this.render(this.el.parentNode);
22851         }else{
22852             s.parentNode.removeChild(s); // remove it
22853         }
22854
22855     }
22856     if (this.store) {
22857         this.store = Roo.factory(this.store, Roo.data);
22858     }
22859     
22860     this.selectedIndex = -1;
22861     if(this.mode == 'local'){
22862         if(config.queryDelay === undefined){
22863             this.queryDelay = 10;
22864         }
22865         if(config.minChars === undefined){
22866             this.minChars = 0;
22867         }
22868     }
22869 };
22870
22871 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22872     /**
22873      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22874      */
22875     /**
22876      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22877      * rendering into an Roo.Editor, defaults to false)
22878      */
22879     /**
22880      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22881      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22882      */
22883     /**
22884      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22885      */
22886     /**
22887      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22888      * the dropdown list (defaults to undefined, with no header element)
22889      */
22890
22891      /**
22892      * @cfg {String/Roo.Template} tpl The template to use to render the output
22893      */
22894      
22895     // private
22896     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22897     /**
22898      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22899      */
22900     listWidth: undefined,
22901     /**
22902      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22903      * mode = 'remote' or 'text' if mode = 'local')
22904      */
22905     displayField: undefined,
22906     /**
22907      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22908      * mode = 'remote' or 'value' if mode = 'local'). 
22909      * Note: use of a valueField requires the user make a selection
22910      * in order for a value to be mapped.
22911      */
22912     valueField: undefined,
22913     
22914     
22915     /**
22916      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22917      * field's data value (defaults to the underlying DOM element's name)
22918      */
22919     hiddenName: undefined,
22920     /**
22921      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22922      */
22923     listClass: '',
22924     /**
22925      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22926      */
22927     selectedClass: 'x-combo-selected',
22928     /**
22929      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22930      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22931      * which displays a downward arrow icon).
22932      */
22933     triggerClass : 'x-form-arrow-trigger',
22934     /**
22935      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22936      */
22937     shadow:'sides',
22938     /**
22939      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22940      * anchor positions (defaults to 'tl-bl')
22941      */
22942     listAlign: 'tl-bl?',
22943     /**
22944      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22945      */
22946     maxHeight: 300,
22947     /**
22948      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22949      * query specified by the allQuery config option (defaults to 'query')
22950      */
22951     triggerAction: 'query',
22952     /**
22953      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22954      * (defaults to 4, does not apply if editable = false)
22955      */
22956     minChars : 4,
22957     /**
22958      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22959      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22960      */
22961     typeAhead: false,
22962     /**
22963      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22964      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22965      */
22966     queryDelay: 500,
22967     /**
22968      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22969      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22970      */
22971     pageSize: 0,
22972     /**
22973      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22974      * when editable = true (defaults to false)
22975      */
22976     selectOnFocus:false,
22977     /**
22978      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22979      */
22980     queryParam: 'query',
22981     /**
22982      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22983      * when mode = 'remote' (defaults to 'Loading...')
22984      */
22985     loadingText: 'Loading...',
22986     /**
22987      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22988      */
22989     resizable: false,
22990     /**
22991      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22992      */
22993     handleHeight : 8,
22994     /**
22995      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22996      * traditional select (defaults to true)
22997      */
22998     editable: true,
22999     /**
23000      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23001      */
23002     allQuery: '',
23003     /**
23004      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23005      */
23006     mode: 'remote',
23007     /**
23008      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23009      * listWidth has a higher value)
23010      */
23011     minListWidth : 70,
23012     /**
23013      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23014      * allow the user to set arbitrary text into the field (defaults to false)
23015      */
23016     forceSelection:false,
23017     /**
23018      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23019      * if typeAhead = true (defaults to 250)
23020      */
23021     typeAheadDelay : 250,
23022     /**
23023      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23024      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23025      */
23026     valueNotFoundText : undefined,
23027     /**
23028      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23029      */
23030     blockFocus : false,
23031     
23032     /**
23033      * @cfg {Boolean} disableClear Disable showing of clear button.
23034      */
23035     disableClear : false,
23036     /**
23037      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23038      */
23039     alwaysQuery : false,
23040     
23041     //private
23042     addicon : false,
23043     editicon: false,
23044     
23045     // element that contains real text value.. (when hidden is used..)
23046      
23047     // private
23048     onRender : function(ct, position){
23049         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23050         if(this.hiddenName){
23051             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23052                     'before', true);
23053             this.hiddenField.value =
23054                 this.hiddenValue !== undefined ? this.hiddenValue :
23055                 this.value !== undefined ? this.value : '';
23056
23057             // prevent input submission
23058             this.el.dom.removeAttribute('name');
23059              
23060              
23061         }
23062         if(Roo.isGecko){
23063             this.el.dom.setAttribute('autocomplete', 'off');
23064         }
23065
23066         var cls = 'x-combo-list';
23067
23068         this.list = new Roo.Layer({
23069             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23070         });
23071
23072         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23073         this.list.setWidth(lw);
23074         this.list.swallowEvent('mousewheel');
23075         this.assetHeight = 0;
23076
23077         if(this.title){
23078             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23079             this.assetHeight += this.header.getHeight();
23080         }
23081
23082         this.innerList = this.list.createChild({cls:cls+'-inner'});
23083         this.innerList.on('mouseover', this.onViewOver, this);
23084         this.innerList.on('mousemove', this.onViewMove, this);
23085         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23086         
23087         if(this.allowBlank && !this.pageSize && !this.disableClear){
23088             this.footer = this.list.createChild({cls:cls+'-ft'});
23089             this.pageTb = new Roo.Toolbar(this.footer);
23090            
23091         }
23092         if(this.pageSize){
23093             this.footer = this.list.createChild({cls:cls+'-ft'});
23094             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23095                     {pageSize: this.pageSize});
23096             
23097         }
23098         
23099         if (this.pageTb && this.allowBlank && !this.disableClear) {
23100             var _this = this;
23101             this.pageTb.add(new Roo.Toolbar.Fill(), {
23102                 cls: 'x-btn-icon x-btn-clear',
23103                 text: '&#160;',
23104                 handler: function()
23105                 {
23106                     _this.collapse();
23107                     _this.clearValue();
23108                     _this.onSelect(false, -1);
23109                 }
23110             });
23111         }
23112         if (this.footer) {
23113             this.assetHeight += this.footer.getHeight();
23114         }
23115         
23116
23117         if(!this.tpl){
23118             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23119         }
23120
23121         this.view = new Roo.View(this.innerList, this.tpl, {
23122             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23123         });
23124
23125         this.view.on('click', this.onViewClick, this);
23126
23127         this.store.on('beforeload', this.onBeforeLoad, this);
23128         this.store.on('load', this.onLoad, this);
23129         this.store.on('loadexception', this.onLoadException, this);
23130
23131         if(this.resizable){
23132             this.resizer = new Roo.Resizable(this.list,  {
23133                pinned:true, handles:'se'
23134             });
23135             this.resizer.on('resize', function(r, w, h){
23136                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23137                 this.listWidth = w;
23138                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23139                 this.restrictHeight();
23140             }, this);
23141             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23142         }
23143         if(!this.editable){
23144             this.editable = true;
23145             this.setEditable(false);
23146         }  
23147         
23148         
23149         if (typeof(this.events.add.listeners) != 'undefined') {
23150             
23151             this.addicon = this.wrap.createChild(
23152                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23153        
23154             this.addicon.on('click', function(e) {
23155                 this.fireEvent('add', this);
23156             }, this);
23157         }
23158         if (typeof(this.events.edit.listeners) != 'undefined') {
23159             
23160             this.editicon = this.wrap.createChild(
23161                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23162             if (this.addicon) {
23163                 this.editicon.setStyle('margin-left', '40px');
23164             }
23165             this.editicon.on('click', function(e) {
23166                 
23167                 // we fire even  if inothing is selected..
23168                 this.fireEvent('edit', this, this.lastData );
23169                 
23170             }, this);
23171         }
23172         
23173         
23174         
23175     },
23176
23177     // private
23178     initEvents : function(){
23179         Roo.form.ComboBox.superclass.initEvents.call(this);
23180
23181         this.keyNav = new Roo.KeyNav(this.el, {
23182             "up" : function(e){
23183                 this.inKeyMode = true;
23184                 this.selectPrev();
23185             },
23186
23187             "down" : function(e){
23188                 if(!this.isExpanded()){
23189                     this.onTriggerClick();
23190                 }else{
23191                     this.inKeyMode = true;
23192                     this.selectNext();
23193                 }
23194             },
23195
23196             "enter" : function(e){
23197                 this.onViewClick();
23198                 //return true;
23199             },
23200
23201             "esc" : function(e){
23202                 this.collapse();
23203             },
23204
23205             "tab" : function(e){
23206                 this.onViewClick(false);
23207                 this.fireEvent("specialkey", this, e);
23208                 return true;
23209             },
23210
23211             scope : this,
23212
23213             doRelay : function(foo, bar, hname){
23214                 if(hname == 'down' || this.scope.isExpanded()){
23215                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23216                 }
23217                 return true;
23218             },
23219
23220             forceKeyDown: true
23221         });
23222         this.queryDelay = Math.max(this.queryDelay || 10,
23223                 this.mode == 'local' ? 10 : 250);
23224         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23225         if(this.typeAhead){
23226             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23227         }
23228         if(this.editable !== false){
23229             this.el.on("keyup", this.onKeyUp, this);
23230         }
23231         if(this.forceSelection){
23232             this.on('blur', this.doForce, this);
23233         }
23234     },
23235
23236     onDestroy : function(){
23237         if(this.view){
23238             this.view.setStore(null);
23239             this.view.el.removeAllListeners();
23240             this.view.el.remove();
23241             this.view.purgeListeners();
23242         }
23243         if(this.list){
23244             this.list.destroy();
23245         }
23246         if(this.store){
23247             this.store.un('beforeload', this.onBeforeLoad, this);
23248             this.store.un('load', this.onLoad, this);
23249             this.store.un('loadexception', this.onLoadException, this);
23250         }
23251         Roo.form.ComboBox.superclass.onDestroy.call(this);
23252     },
23253
23254     // private
23255     fireKey : function(e){
23256         if(e.isNavKeyPress() && !this.list.isVisible()){
23257             this.fireEvent("specialkey", this, e);
23258         }
23259     },
23260
23261     // private
23262     onResize: function(w, h){
23263         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23264         
23265         if(typeof w != 'number'){
23266             // we do not handle it!?!?
23267             return;
23268         }
23269         var tw = this.trigger.getWidth();
23270         tw += this.addicon ? this.addicon.getWidth() : 0;
23271         tw += this.editicon ? this.editicon.getWidth() : 0;
23272         var x = w - tw;
23273         this.el.setWidth( this.adjustWidth('input', x));
23274             
23275         this.trigger.setStyle('left', x+'px');
23276         
23277         if(this.list && this.listWidth === undefined){
23278             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23279             this.list.setWidth(lw);
23280             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23281         }
23282         
23283     
23284         
23285     },
23286
23287     /**
23288      * Allow or prevent the user from directly editing the field text.  If false is passed,
23289      * the user will only be able to select from the items defined in the dropdown list.  This method
23290      * is the runtime equivalent of setting the 'editable' config option at config time.
23291      * @param {Boolean} value True to allow the user to directly edit the field text
23292      */
23293     setEditable : function(value){
23294         if(value == this.editable){
23295             return;
23296         }
23297         this.editable = value;
23298         if(!value){
23299             this.el.dom.setAttribute('readOnly', true);
23300             this.el.on('mousedown', this.onTriggerClick,  this);
23301             this.el.addClass('x-combo-noedit');
23302         }else{
23303             this.el.dom.setAttribute('readOnly', false);
23304             this.el.un('mousedown', this.onTriggerClick,  this);
23305             this.el.removeClass('x-combo-noedit');
23306         }
23307     },
23308
23309     // private
23310     onBeforeLoad : function(){
23311         if(!this.hasFocus){
23312             return;
23313         }
23314         this.innerList.update(this.loadingText ?
23315                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23316         this.restrictHeight();
23317         this.selectedIndex = -1;
23318     },
23319
23320     // private
23321     onLoad : function(){
23322         if(!this.hasFocus){
23323             return;
23324         }
23325         if(this.store.getCount() > 0){
23326             this.expand();
23327             this.restrictHeight();
23328             if(this.lastQuery == this.allQuery){
23329                 if(this.editable){
23330                     this.el.dom.select();
23331                 }
23332                 if(!this.selectByValue(this.value, true)){
23333                     this.select(0, true);
23334                 }
23335             }else{
23336                 this.selectNext();
23337                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23338                     this.taTask.delay(this.typeAheadDelay);
23339                 }
23340             }
23341         }else{
23342             this.onEmptyResults();
23343         }
23344         //this.el.focus();
23345     },
23346     // private
23347     onLoadException : function()
23348     {
23349         this.collapse();
23350         Roo.log(this.store.reader.jsonData);
23351         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23352             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23353         }
23354         
23355         
23356     },
23357     // private
23358     onTypeAhead : function(){
23359         if(this.store.getCount() > 0){
23360             var r = this.store.getAt(0);
23361             var newValue = r.data[this.displayField];
23362             var len = newValue.length;
23363             var selStart = this.getRawValue().length;
23364             if(selStart != len){
23365                 this.setRawValue(newValue);
23366                 this.selectText(selStart, newValue.length);
23367             }
23368         }
23369     },
23370
23371     // private
23372     onSelect : function(record, index){
23373         if(this.fireEvent('beforeselect', this, record, index) !== false){
23374             this.setFromData(index > -1 ? record.data : false);
23375             this.collapse();
23376             this.fireEvent('select', this, record, index);
23377         }
23378     },
23379
23380     /**
23381      * Returns the currently selected field value or empty string if no value is set.
23382      * @return {String} value The selected value
23383      */
23384     getValue : function(){
23385         if(this.valueField){
23386             return typeof this.value != 'undefined' ? this.value : '';
23387         }else{
23388             return Roo.form.ComboBox.superclass.getValue.call(this);
23389         }
23390     },
23391
23392     /**
23393      * Clears any text/value currently set in the field
23394      */
23395     clearValue : function(){
23396         if(this.hiddenField){
23397             this.hiddenField.value = '';
23398         }
23399         this.value = '';
23400         this.setRawValue('');
23401         this.lastSelectionText = '';
23402         
23403     },
23404
23405     /**
23406      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23407      * will be displayed in the field.  If the value does not match the data value of an existing item,
23408      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23409      * Otherwise the field will be blank (although the value will still be set).
23410      * @param {String} value The value to match
23411      */
23412     setValue : function(v){
23413         var text = v;
23414         if(this.valueField){
23415             var r = this.findRecord(this.valueField, v);
23416             if(r){
23417                 text = r.data[this.displayField];
23418             }else if(this.valueNotFoundText !== undefined){
23419                 text = this.valueNotFoundText;
23420             }
23421         }
23422         this.lastSelectionText = text;
23423         if(this.hiddenField){
23424             this.hiddenField.value = v;
23425         }
23426         Roo.form.ComboBox.superclass.setValue.call(this, text);
23427         this.value = v;
23428     },
23429     /**
23430      * @property {Object} the last set data for the element
23431      */
23432     
23433     lastData : false,
23434     /**
23435      * Sets the value of the field based on a object which is related to the record format for the store.
23436      * @param {Object} value the value to set as. or false on reset?
23437      */
23438     setFromData : function(o){
23439         var dv = ''; // display value
23440         var vv = ''; // value value..
23441         this.lastData = o;
23442         if (this.displayField) {
23443             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23444         } else {
23445             // this is an error condition!!!
23446             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23447         }
23448         
23449         if(this.valueField){
23450             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23451         }
23452         if(this.hiddenField){
23453             this.hiddenField.value = vv;
23454             
23455             this.lastSelectionText = dv;
23456             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23457             this.value = vv;
23458             return;
23459         }
23460         // no hidden field.. - we store the value in 'value', but still display
23461         // display field!!!!
23462         this.lastSelectionText = dv;
23463         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23464         this.value = vv;
23465         
23466         
23467     },
23468     // private
23469     reset : function(){
23470         // overridden so that last data is reset..
23471         this.setValue(this.resetValue);
23472         this.clearInvalid();
23473         this.lastData = false;
23474         if (this.view) {
23475             this.view.clearSelections();
23476         }
23477     },
23478     // private
23479     findRecord : function(prop, value){
23480         var record;
23481         if(this.store.getCount() > 0){
23482             this.store.each(function(r){
23483                 if(r.data[prop] == value){
23484                     record = r;
23485                     return false;
23486                 }
23487                 return true;
23488             });
23489         }
23490         return record;
23491     },
23492     
23493     getName: function()
23494     {
23495         // returns hidden if it's set..
23496         if (!this.rendered) {return ''};
23497         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23498         
23499     },
23500     // private
23501     onViewMove : function(e, t){
23502         this.inKeyMode = false;
23503     },
23504
23505     // private
23506     onViewOver : function(e, t){
23507         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23508             return;
23509         }
23510         var item = this.view.findItemFromChild(t);
23511         if(item){
23512             var index = this.view.indexOf(item);
23513             this.select(index, false);
23514         }
23515     },
23516
23517     // private
23518     onViewClick : function(doFocus)
23519     {
23520         var index = this.view.getSelectedIndexes()[0];
23521         var r = this.store.getAt(index);
23522         if(r){
23523             this.onSelect(r, index);
23524         }
23525         if(doFocus !== false && !this.blockFocus){
23526             this.el.focus();
23527         }
23528     },
23529
23530     // private
23531     restrictHeight : function(){
23532         this.innerList.dom.style.height = '';
23533         var inner = this.innerList.dom;
23534         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23535         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23536         this.list.beginUpdate();
23537         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23538         this.list.alignTo(this.el, this.listAlign);
23539         this.list.endUpdate();
23540     },
23541
23542     // private
23543     onEmptyResults : function(){
23544         this.collapse();
23545     },
23546
23547     /**
23548      * Returns true if the dropdown list is expanded, else false.
23549      */
23550     isExpanded : function(){
23551         return this.list.isVisible();
23552     },
23553
23554     /**
23555      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23556      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23557      * @param {String} value The data value of the item to select
23558      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23559      * selected item if it is not currently in view (defaults to true)
23560      * @return {Boolean} True if the value matched an item in the list, else false
23561      */
23562     selectByValue : function(v, scrollIntoView){
23563         if(v !== undefined && v !== null){
23564             var r = this.findRecord(this.valueField || this.displayField, v);
23565             if(r){
23566                 this.select(this.store.indexOf(r), scrollIntoView);
23567                 return true;
23568             }
23569         }
23570         return false;
23571     },
23572
23573     /**
23574      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23575      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23576      * @param {Number} index The zero-based index of the list item to select
23577      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23578      * selected item if it is not currently in view (defaults to true)
23579      */
23580     select : function(index, scrollIntoView){
23581         this.selectedIndex = index;
23582         this.view.select(index);
23583         if(scrollIntoView !== false){
23584             var el = this.view.getNode(index);
23585             if(el){
23586                 this.innerList.scrollChildIntoView(el, false);
23587             }
23588         }
23589     },
23590
23591     // private
23592     selectNext : function(){
23593         var ct = this.store.getCount();
23594         if(ct > 0){
23595             if(this.selectedIndex == -1){
23596                 this.select(0);
23597             }else if(this.selectedIndex < ct-1){
23598                 this.select(this.selectedIndex+1);
23599             }
23600         }
23601     },
23602
23603     // private
23604     selectPrev : function(){
23605         var ct = this.store.getCount();
23606         if(ct > 0){
23607             if(this.selectedIndex == -1){
23608                 this.select(0);
23609             }else if(this.selectedIndex != 0){
23610                 this.select(this.selectedIndex-1);
23611             }
23612         }
23613     },
23614
23615     // private
23616     onKeyUp : function(e){
23617         if(this.editable !== false && !e.isSpecialKey()){
23618             this.lastKey = e.getKey();
23619             this.dqTask.delay(this.queryDelay);
23620         }
23621     },
23622
23623     // private
23624     validateBlur : function(){
23625         return !this.list || !this.list.isVisible();   
23626     },
23627
23628     // private
23629     initQuery : function(){
23630         this.doQuery(this.getRawValue());
23631     },
23632
23633     // private
23634     doForce : function(){
23635         if(this.el.dom.value.length > 0){
23636             this.el.dom.value =
23637                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23638              
23639         }
23640     },
23641
23642     /**
23643      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23644      * query allowing the query action to be canceled if needed.
23645      * @param {String} query The SQL query to execute
23646      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23647      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23648      * saved in the current store (defaults to false)
23649      */
23650     doQuery : function(q, forceAll){
23651         if(q === undefined || q === null){
23652             q = '';
23653         }
23654         var qe = {
23655             query: q,
23656             forceAll: forceAll,
23657             combo: this,
23658             cancel:false
23659         };
23660         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23661             return false;
23662         }
23663         q = qe.query;
23664         forceAll = qe.forceAll;
23665         if(forceAll === true || (q.length >= this.minChars)){
23666             if(this.lastQuery != q || this.alwaysQuery){
23667                 this.lastQuery = q;
23668                 if(this.mode == 'local'){
23669                     this.selectedIndex = -1;
23670                     if(forceAll){
23671                         this.store.clearFilter();
23672                     }else{
23673                         this.store.filter(this.displayField, q);
23674                     }
23675                     this.onLoad();
23676                 }else{
23677                     this.store.baseParams[this.queryParam] = q;
23678                     this.store.load({
23679                         params: this.getParams(q)
23680                     });
23681                     this.expand();
23682                 }
23683             }else{
23684                 this.selectedIndex = -1;
23685                 this.onLoad();   
23686             }
23687         }
23688     },
23689
23690     // private
23691     getParams : function(q){
23692         var p = {};
23693         //p[this.queryParam] = q;
23694         if(this.pageSize){
23695             p.start = 0;
23696             p.limit = this.pageSize;
23697         }
23698         return p;
23699     },
23700
23701     /**
23702      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23703      */
23704     collapse : function(){
23705         if(!this.isExpanded()){
23706             return;
23707         }
23708         this.list.hide();
23709         Roo.get(document).un('mousedown', this.collapseIf, this);
23710         Roo.get(document).un('mousewheel', this.collapseIf, this);
23711         if (!this.editable) {
23712             Roo.get(document).un('keydown', this.listKeyPress, this);
23713         }
23714         this.fireEvent('collapse', this);
23715     },
23716
23717     // private
23718     collapseIf : function(e){
23719         if(!e.within(this.wrap) && !e.within(this.list)){
23720             this.collapse();
23721         }
23722     },
23723
23724     /**
23725      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23726      */
23727     expand : function(){
23728         if(this.isExpanded() || !this.hasFocus){
23729             return;
23730         }
23731         this.list.alignTo(this.el, this.listAlign);
23732         this.list.show();
23733         Roo.get(document).on('mousedown', this.collapseIf, this);
23734         Roo.get(document).on('mousewheel', this.collapseIf, this);
23735         if (!this.editable) {
23736             Roo.get(document).on('keydown', this.listKeyPress, this);
23737         }
23738         
23739         this.fireEvent('expand', this);
23740     },
23741
23742     // private
23743     // Implements the default empty TriggerField.onTriggerClick function
23744     onTriggerClick : function(){
23745         if(this.disabled){
23746             return;
23747         }
23748         if(this.isExpanded()){
23749             this.collapse();
23750             if (!this.blockFocus) {
23751                 this.el.focus();
23752             }
23753             
23754         }else {
23755             this.hasFocus = true;
23756             if(this.triggerAction == 'all') {
23757                 this.doQuery(this.allQuery, true);
23758             } else {
23759                 this.doQuery(this.getRawValue());
23760             }
23761             if (!this.blockFocus) {
23762                 this.el.focus();
23763             }
23764         }
23765     },
23766     listKeyPress : function(e)
23767     {
23768         //Roo.log('listkeypress');
23769         // scroll to first matching element based on key pres..
23770         if (e.isSpecialKey()) {
23771             return false;
23772         }
23773         var k = String.fromCharCode(e.getKey()).toUpperCase();
23774         //Roo.log(k);
23775         var match  = false;
23776         var csel = this.view.getSelectedNodes();
23777         var cselitem = false;
23778         if (csel.length) {
23779             var ix = this.view.indexOf(csel[0]);
23780             cselitem  = this.store.getAt(ix);
23781             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23782                 cselitem = false;
23783             }
23784             
23785         }
23786         
23787         this.store.each(function(v) { 
23788             if (cselitem) {
23789                 // start at existing selection.
23790                 if (cselitem.id == v.id) {
23791                     cselitem = false;
23792                 }
23793                 return;
23794             }
23795                 
23796             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23797                 match = this.store.indexOf(v);
23798                 return false;
23799             }
23800         }, this);
23801         
23802         if (match === false) {
23803             return true; // no more action?
23804         }
23805         // scroll to?
23806         this.view.select(match);
23807         var sn = Roo.get(this.view.getSelectedNodes()[0])
23808         sn.scrollIntoView(sn.dom.parentNode, false);
23809     }
23810
23811     /** 
23812     * @cfg {Boolean} grow 
23813     * @hide 
23814     */
23815     /** 
23816     * @cfg {Number} growMin 
23817     * @hide 
23818     */
23819     /** 
23820     * @cfg {Number} growMax 
23821     * @hide 
23822     */
23823     /**
23824      * @hide
23825      * @method autoSize
23826      */
23827 });/*
23828  * Copyright(c) 2010-2012, Roo J Solutions Limited
23829  *
23830  * Licence LGPL
23831  *
23832  */
23833
23834 /**
23835  * @class Roo.form.ComboBoxArray
23836  * @extends Roo.form.TextField
23837  * A facebook style adder... for lists of email / people / countries  etc...
23838  * pick multiple items from a combo box, and shows each one.
23839  *
23840  *  Fred [x]  Brian [x]  [Pick another |v]
23841  *
23842  *
23843  *  For this to work: it needs various extra information
23844  *    - normal combo problay has
23845  *      name, hiddenName
23846  *    + displayField, valueField
23847  *
23848  *    For our purpose...
23849  *
23850  *
23851  *   If we change from 'extends' to wrapping...
23852  *   
23853  *  
23854  *
23855  
23856  
23857  * @constructor
23858  * Create a new ComboBoxArray.
23859  * @param {Object} config Configuration options
23860  */
23861  
23862
23863 Roo.form.ComboBoxArray = function(config)
23864 {
23865     this.addEvents({
23866         /**
23867          * @event remove
23868          * Fires when remove the value from the list
23869              * @param {Roo.form.ComboBoxArray} _self This combo box array
23870              * @param {Roo.form.ComboBoxArray.Item} item removed item
23871              */
23872         'remove' : true
23873         
23874         
23875     });
23876     
23877     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23878     
23879     this.items = new Roo.util.MixedCollection(false);
23880     
23881     // construct the child combo...
23882     
23883     
23884     
23885     
23886    
23887     
23888 }
23889
23890  
23891 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23892
23893     /**
23894      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23895      */
23896     
23897     lastData : false,
23898     
23899     // behavies liek a hiddne field
23900     inputType:      'hidden',
23901     /**
23902      * @cfg {Number} width The width of the box that displays the selected element
23903      */ 
23904     width:          300,
23905
23906     
23907     
23908     /**
23909      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
23910      */
23911     name : false,
23912     /**
23913      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
23914      */
23915     hiddenName : false,
23916     
23917     
23918     // private the array of items that are displayed..
23919     items  : false,
23920     // private - the hidden field el.
23921     hiddenEl : false,
23922     // private - the filed el..
23923     el : false,
23924     
23925     //validateValue : function() { return true; }, // all values are ok!
23926     //onAddClick: function() { },
23927     
23928     onRender : function(ct, position) 
23929     {
23930         
23931         // create the standard hidden element
23932         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
23933         
23934         
23935         // give fake names to child combo;
23936         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
23937         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
23938         
23939         this.combo = Roo.factory(this.combo, Roo.form);
23940         this.combo.onRender(ct, position);
23941         if (typeof(this.combo.width) != 'undefined') {
23942             this.combo.onResize(this.combo.width,0);
23943         }
23944         
23945         this.combo.initEvents();
23946         
23947         // assigned so form know we need to do this..
23948         this.store          = this.combo.store;
23949         this.valueField     = this.combo.valueField;
23950         this.displayField   = this.combo.displayField ;
23951         
23952         
23953         this.combo.wrap.addClass('x-cbarray-grp');
23954         
23955         var cbwrap = this.combo.wrap.createChild(
23956             {tag: 'div', cls: 'x-cbarray-cb'},
23957             this.combo.el.dom
23958         );
23959         
23960              
23961         this.hiddenEl = this.combo.wrap.createChild({
23962             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
23963         });
23964         this.el = this.combo.wrap.createChild({
23965             tag: 'input',  type:'hidden' , name: this.name, value : ''
23966         });
23967          //   this.el.dom.removeAttribute("name");
23968         
23969         
23970         this.outerWrap = this.combo.wrap;
23971         this.wrap = cbwrap;
23972         
23973         this.outerWrap.setWidth(this.width);
23974         this.outerWrap.dom.removeChild(this.el.dom);
23975         
23976         this.wrap.dom.appendChild(this.el.dom);
23977         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
23978         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
23979         
23980         this.combo.trigger.setStyle('position','relative');
23981         this.combo.trigger.setStyle('left', '0px');
23982         this.combo.trigger.setStyle('top', '2px');
23983         
23984         this.combo.el.setStyle('vertical-align', 'text-bottom');
23985         
23986         //this.trigger.setStyle('vertical-align', 'top');
23987         
23988         // this should use the code from combo really... on('add' ....)
23989         if (this.adder) {
23990             
23991         
23992             this.adder = this.outerWrap.createChild(
23993                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
23994             var _t = this;
23995             this.adder.on('click', function(e) {
23996                 _t.fireEvent('adderclick', this, e);
23997             }, _t);
23998         }
23999         //var _t = this;
24000         //this.adder.on('click', this.onAddClick, _t);
24001         
24002         
24003         this.combo.on('select', function(cb, rec, ix) {
24004             this.addItem(rec.data);
24005             
24006             cb.setValue('');
24007             cb.el.dom.value = '';
24008             //cb.lastData = rec.data;
24009             // add to list
24010             
24011         }, this);
24012         
24013         
24014     },
24015     
24016     
24017     getName: function()
24018     {
24019         // returns hidden if it's set..
24020         if (!this.rendered) {return ''};
24021         return  this.hiddenName ? this.hiddenName : this.name;
24022         
24023     },
24024     
24025     
24026     onResize: function(w, h){
24027         
24028         return;
24029         // not sure if this is needed..
24030         //this.combo.onResize(w,h);
24031         
24032         if(typeof w != 'number'){
24033             // we do not handle it!?!?
24034             return;
24035         }
24036         var tw = this.combo.trigger.getWidth();
24037         tw += this.addicon ? this.addicon.getWidth() : 0;
24038         tw += this.editicon ? this.editicon.getWidth() : 0;
24039         var x = w - tw;
24040         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24041             
24042         this.combo.trigger.setStyle('left', '0px');
24043         
24044         if(this.list && this.listWidth === undefined){
24045             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24046             this.list.setWidth(lw);
24047             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24048         }
24049         
24050     
24051         
24052     },
24053     
24054     addItem: function(rec)
24055     {
24056         var valueField = this.combo.valueField;
24057         var displayField = this.combo.displayField;
24058         if (this.items.indexOfKey(rec[valueField]) > -1) {
24059             //console.log("GOT " + rec.data.id);
24060             return;
24061         }
24062         
24063         var x = new Roo.form.ComboBoxArray.Item({
24064             //id : rec[this.idField],
24065             data : rec,
24066             displayField : displayField ,
24067             tipField : displayField ,
24068             cb : this
24069         });
24070         // use the 
24071         this.items.add(rec[valueField],x);
24072         // add it before the element..
24073         this.updateHiddenEl();
24074         x.render(this.outerWrap, this.wrap.dom);
24075         // add the image handler..
24076     },
24077     
24078     updateHiddenEl : function()
24079     {
24080         this.validate();
24081         if (!this.hiddenEl) {
24082             return;
24083         }
24084         var ar = [];
24085         var idField = this.combo.valueField;
24086         
24087         this.items.each(function(f) {
24088             ar.push(f.data[idField]);
24089            
24090         });
24091         this.hiddenEl.dom.value = ar.join(',');
24092         this.validate();
24093     },
24094     
24095     reset : function()
24096     {
24097         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24098         this.items.each(function(f) {
24099            f.remove(); 
24100         });
24101         this.el.dom.value = '';
24102         if (this.hiddenEl) {
24103             this.hiddenEl.dom.value = '';
24104         }
24105         
24106     },
24107     getValue: function()
24108     {
24109         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24110     },
24111     setValue: function(v) // not a valid action - must use addItems..
24112     {
24113          
24114         this.reset();
24115         
24116         
24117         
24118         if (this.store.isLocal && (typeof(v) == 'string')) {
24119             // then we can use the store to find the values..
24120             // comma seperated at present.. this needs to allow JSON based encoding..
24121             this.hiddenEl.value  = v;
24122             var v_ar = [];
24123             Roo.each(v.split(','), function(k) {
24124                 Roo.log("CHECK " + this.valueField + ',' + k);
24125                 var li = this.store.query(this.valueField, k);
24126                 if (!li.length) {
24127                     return;
24128                 }
24129                 var add = {};
24130                 add[this.valueField] = k;
24131                 add[this.displayField] = li.item(0).data[this.displayField];
24132                 
24133                 this.addItem(add);
24134             }, this) 
24135              
24136         }
24137         if (typeof(v) == 'object') {
24138             // then let's assume it's an array of objects..
24139             Roo.each(v, function(l) {
24140                 this.addItem(l);
24141             }, this);
24142              
24143         }
24144         
24145         
24146     },
24147     setFromData: function(v)
24148     {
24149         // this recieves an object, if setValues is called.
24150         this.reset();
24151         this.el.dom.value = v[this.displayField];
24152         this.hiddenEl.dom.value = v[this.valueField];
24153         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24154             return;
24155         }
24156         var kv = v[this.valueField];
24157         var dv = v[this.displayField];
24158         kv = typeof(kv) != 'string' ? '' : kv;
24159         dv = typeof(dv) != 'string' ? '' : dv;
24160         
24161         
24162         var keys = kv.split(',');
24163         var display = dv.split(',');
24164         for (var i = 0 ; i < keys.length; i++) {
24165             
24166             add = {};
24167             add[this.valueField] = keys[i];
24168             add[this.displayField] = display[i];
24169             this.addItem(add);
24170         }
24171       
24172         
24173     },
24174     
24175     /**
24176      * Validates the combox array value
24177      * @return {Boolean} True if the value is valid, else false
24178      */
24179     validate : function(){
24180         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24181             this.clearInvalid();
24182             return true;
24183         }
24184         return false;
24185     },
24186     
24187     validateValue : function(value){
24188         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24189         
24190     },
24191     
24192     /*@
24193      * overide
24194      * 
24195      */
24196     isDirty : function() {
24197         if(this.disabled) {
24198             return false;
24199         }
24200         
24201         try {
24202             var d = Roo.decode(String(this.originalValue));
24203         } catch (e) {
24204             return String(this.getValue()) !== String(this.originalValue);
24205         }
24206         
24207         var originalValue = [];
24208         
24209         for (var i = 0; i < d.length; i++){
24210             originalValue.push(d[i][this.valueField]);
24211         }
24212         
24213         return String(this.getValue()) !== String(originalValue.join(','));
24214         
24215     }
24216     
24217 });
24218
24219
24220
24221 /**
24222  * @class Roo.form.ComboBoxArray.Item
24223  * @extends Roo.BoxComponent
24224  * A selected item in the list
24225  *  Fred [x]  Brian [x]  [Pick another |v]
24226  * 
24227  * @constructor
24228  * Create a new item.
24229  * @param {Object} config Configuration options
24230  */
24231  
24232 Roo.form.ComboBoxArray.Item = function(config) {
24233     config.id = Roo.id();
24234     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24235 }
24236
24237 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24238     data : {},
24239     cb: false,
24240     displayField : false,
24241     tipField : false,
24242     
24243     
24244     defaultAutoCreate : {
24245         tag: 'div',
24246         cls: 'x-cbarray-item',
24247         cn : [ 
24248             { tag: 'div' },
24249             {
24250                 tag: 'img',
24251                 width:16,
24252                 height : 16,
24253                 src : Roo.BLANK_IMAGE_URL ,
24254                 align: 'center'
24255             }
24256         ]
24257         
24258     },
24259     
24260  
24261     onRender : function(ct, position)
24262     {
24263         Roo.form.Field.superclass.onRender.call(this, ct, position);
24264         
24265         if(!this.el){
24266             var cfg = this.getAutoCreate();
24267             this.el = ct.createChild(cfg, position);
24268         }
24269         
24270         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24271         
24272         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24273             this.cb.renderer(this.data) :
24274             String.format('{0}',this.data[this.displayField]);
24275         
24276             
24277         this.el.child('div').dom.setAttribute('qtip',
24278                         String.format('{0}',this.data[this.tipField])
24279         );
24280         
24281         this.el.child('img').on('click', this.remove, this);
24282         
24283     },
24284    
24285     remove : function()
24286     {
24287         this.cb.items.remove(this);
24288         this.el.child('img').un('click', this.remove, this);
24289         this.el.remove();
24290         this.cb.updateHiddenEl();
24291         
24292         this.cb.fireEvent('remove', this.cb, this);
24293     }
24294 });/*
24295  * Based on:
24296  * Ext JS Library 1.1.1
24297  * Copyright(c) 2006-2007, Ext JS, LLC.
24298  *
24299  * Originally Released Under LGPL - original licence link has changed is not relivant.
24300  *
24301  * Fork - LGPL
24302  * <script type="text/javascript">
24303  */
24304 /**
24305  * @class Roo.form.Checkbox
24306  * @extends Roo.form.Field
24307  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24308  * @constructor
24309  * Creates a new Checkbox
24310  * @param {Object} config Configuration options
24311  */
24312 Roo.form.Checkbox = function(config){
24313     Roo.form.Checkbox.superclass.constructor.call(this, config);
24314     this.addEvents({
24315         /**
24316          * @event check
24317          * Fires when the checkbox is checked or unchecked.
24318              * @param {Roo.form.Checkbox} this This checkbox
24319              * @param {Boolean} checked The new checked value
24320              */
24321         check : true
24322     });
24323 };
24324
24325 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24326     /**
24327      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24328      */
24329     focusClass : undefined,
24330     /**
24331      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24332      */
24333     fieldClass: "x-form-field",
24334     /**
24335      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24336      */
24337     checked: false,
24338     /**
24339      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24340      * {tag: "input", type: "checkbox", autocomplete: "off"})
24341      */
24342     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24343     /**
24344      * @cfg {String} boxLabel The text that appears beside the checkbox
24345      */
24346     boxLabel : "",
24347     /**
24348      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24349      */  
24350     inputValue : '1',
24351     /**
24352      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24353      */
24354      valueOff: '0', // value when not checked..
24355
24356     actionMode : 'viewEl', 
24357     //
24358     // private
24359     itemCls : 'x-menu-check-item x-form-item',
24360     groupClass : 'x-menu-group-item',
24361     inputType : 'hidden',
24362     
24363     
24364     inSetChecked: false, // check that we are not calling self...
24365     
24366     inputElement: false, // real input element?
24367     basedOn: false, // ????
24368     
24369     isFormField: true, // not sure where this is needed!!!!
24370
24371     onResize : function(){
24372         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24373         if(!this.boxLabel){
24374             this.el.alignTo(this.wrap, 'c-c');
24375         }
24376     },
24377
24378     initEvents : function(){
24379         Roo.form.Checkbox.superclass.initEvents.call(this);
24380         this.el.on("click", this.onClick,  this);
24381         this.el.on("change", this.onClick,  this);
24382     },
24383
24384
24385     getResizeEl : function(){
24386         return this.wrap;
24387     },
24388
24389     getPositionEl : function(){
24390         return this.wrap;
24391     },
24392
24393     // private
24394     onRender : function(ct, position){
24395         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24396         /*
24397         if(this.inputValue !== undefined){
24398             this.el.dom.value = this.inputValue;
24399         }
24400         */
24401         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24402         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24403         var viewEl = this.wrap.createChild({ 
24404             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24405         this.viewEl = viewEl;   
24406         this.wrap.on('click', this.onClick,  this); 
24407         
24408         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24409         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24410         
24411         
24412         
24413         if(this.boxLabel){
24414             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24415         //    viewEl.on('click', this.onClick,  this); 
24416         }
24417         //if(this.checked){
24418             this.setChecked(this.checked);
24419         //}else{
24420             //this.checked = this.el.dom;
24421         //}
24422
24423     },
24424
24425     // private
24426     initValue : Roo.emptyFn,
24427
24428     /**
24429      * Returns the checked state of the checkbox.
24430      * @return {Boolean} True if checked, else false
24431      */
24432     getValue : function(){
24433         if(this.el){
24434             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24435         }
24436         return this.valueOff;
24437         
24438     },
24439
24440         // private
24441     onClick : function(){ 
24442         this.setChecked(!this.checked);
24443
24444         //if(this.el.dom.checked != this.checked){
24445         //    this.setValue(this.el.dom.checked);
24446        // }
24447     },
24448
24449     /**
24450      * Sets the checked state of the checkbox.
24451      * On is always based on a string comparison between inputValue and the param.
24452      * @param {Boolean/String} value - the value to set 
24453      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24454      */
24455     setValue : function(v,suppressEvent){
24456         
24457         
24458         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24459         //if(this.el && this.el.dom){
24460         //    this.el.dom.checked = this.checked;
24461         //    this.el.dom.defaultChecked = this.checked;
24462         //}
24463         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24464         //this.fireEvent("check", this, this.checked);
24465     },
24466     // private..
24467     setChecked : function(state,suppressEvent)
24468     {
24469         if (this.inSetChecked) {
24470             this.checked = state;
24471             return;
24472         }
24473         
24474     
24475         if(this.wrap){
24476             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24477         }
24478         this.checked = state;
24479         if(suppressEvent !== true){
24480             this.fireEvent('check', this, state);
24481         }
24482         this.inSetChecked = true;
24483         this.el.dom.value = state ? this.inputValue : this.valueOff;
24484         this.inSetChecked = false;
24485         
24486     },
24487     // handle setting of hidden value by some other method!!?!?
24488     setFromHidden: function()
24489     {
24490         if(!this.el){
24491             return;
24492         }
24493         //console.log("SET FROM HIDDEN");
24494         //alert('setFrom hidden');
24495         this.setValue(this.el.dom.value);
24496     },
24497     
24498     onDestroy : function()
24499     {
24500         if(this.viewEl){
24501             Roo.get(this.viewEl).remove();
24502         }
24503          
24504         Roo.form.Checkbox.superclass.onDestroy.call(this);
24505     }
24506
24507 });/*
24508  * Based on:
24509  * Ext JS Library 1.1.1
24510  * Copyright(c) 2006-2007, Ext JS, LLC.
24511  *
24512  * Originally Released Under LGPL - original licence link has changed is not relivant.
24513  *
24514  * Fork - LGPL
24515  * <script type="text/javascript">
24516  */
24517  
24518 /**
24519  * @class Roo.form.Radio
24520  * @extends Roo.form.Checkbox
24521  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24522  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24523  * @constructor
24524  * Creates a new Radio
24525  * @param {Object} config Configuration options
24526  */
24527 Roo.form.Radio = function(){
24528     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24529 };
24530 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24531     inputType: 'radio',
24532
24533     /**
24534      * If this radio is part of a group, it will return the selected value
24535      * @return {String}
24536      */
24537     getGroupValue : function(){
24538         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24539     },
24540     
24541     
24542     onRender : function(ct, position){
24543         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24544         
24545         if(this.inputValue !== undefined){
24546             this.el.dom.value = this.inputValue;
24547         }
24548          
24549         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24550         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24551         //var viewEl = this.wrap.createChild({ 
24552         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24553         //this.viewEl = viewEl;   
24554         //this.wrap.on('click', this.onClick,  this); 
24555         
24556         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24557         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24558         
24559         
24560         
24561         if(this.boxLabel){
24562             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24563         //    viewEl.on('click', this.onClick,  this); 
24564         }
24565          if(this.checked){
24566             this.el.dom.checked =   'checked' ;
24567         }
24568          
24569     } 
24570     
24571     
24572 });//<script type="text/javascript">
24573
24574 /*
24575  * Based  Ext JS Library 1.1.1
24576  * Copyright(c) 2006-2007, Ext JS, LLC.
24577  * LGPL
24578  *
24579  */
24580  
24581 /**
24582  * @class Roo.HtmlEditorCore
24583  * @extends Roo.Component
24584  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24585  *
24586  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24587  */
24588
24589 Roo.HtmlEditorCore = function(config){
24590     
24591     
24592     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24593     this.addEvents({
24594         /**
24595          * @event initialize
24596          * Fires when the editor is fully initialized (including the iframe)
24597          * @param {Roo.HtmlEditorCore} this
24598          */
24599         initialize: true,
24600         /**
24601          * @event activate
24602          * Fires when the editor is first receives the focus. Any insertion must wait
24603          * until after this event.
24604          * @param {Roo.HtmlEditorCore} this
24605          */
24606         activate: true,
24607          /**
24608          * @event beforesync
24609          * Fires before the textarea is updated with content from the editor iframe. Return false
24610          * to cancel the sync.
24611          * @param {Roo.HtmlEditorCore} this
24612          * @param {String} html
24613          */
24614         beforesync: true,
24615          /**
24616          * @event beforepush
24617          * Fires before the iframe editor is updated with content from the textarea. Return false
24618          * to cancel the push.
24619          * @param {Roo.HtmlEditorCore} this
24620          * @param {String} html
24621          */
24622         beforepush: true,
24623          /**
24624          * @event sync
24625          * Fires when the textarea is updated with content from the editor iframe.
24626          * @param {Roo.HtmlEditorCore} this
24627          * @param {String} html
24628          */
24629         sync: true,
24630          /**
24631          * @event push
24632          * Fires when the iframe editor is updated with content from the textarea.
24633          * @param {Roo.HtmlEditorCore} this
24634          * @param {String} html
24635          */
24636         push: true,
24637         
24638         /**
24639          * @event editorevent
24640          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24641          * @param {Roo.HtmlEditorCore} this
24642          */
24643         editorevent: true
24644     });
24645      
24646 };
24647
24648
24649 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24650
24651
24652      /**
24653      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24654      */
24655     
24656     owner : false,
24657     
24658      /**
24659      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24660      *                        Roo.resizable.
24661      */
24662     resizable : false,
24663      /**
24664      * @cfg {Number} height (in pixels)
24665      */   
24666     height: 300,
24667    /**
24668      * @cfg {Number} width (in pixels)
24669      */   
24670     width: 500,
24671     
24672     /**
24673      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24674      * 
24675      */
24676     stylesheets: false,
24677     
24678     // id of frame..
24679     frameId: false,
24680     
24681     // private properties
24682     validationEvent : false,
24683     deferHeight: true,
24684     initialized : false,
24685     activated : false,
24686     sourceEditMode : false,
24687     onFocus : Roo.emptyFn,
24688     iframePad:3,
24689     hideMode:'offsets',
24690     
24691     clearUp: true,
24692     
24693      
24694     
24695
24696     /**
24697      * Protected method that will not generally be called directly. It
24698      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24699      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24700      */
24701     getDocMarkup : function(){
24702         // body styles..
24703         var st = '';
24704         Roo.log(this.stylesheets);
24705         
24706         // inherit styels from page...?? 
24707         if (this.stylesheets === false) {
24708             
24709             Roo.get(document.head).select('style').each(function(node) {
24710                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24711             });
24712             
24713             Roo.get(document.head).select('link').each(function(node) { 
24714                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24715             });
24716             
24717         } else if (!this.stylesheets.length) {
24718                 // simple..
24719                 st = '<style type="text/css">' +
24720                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24721                    '</style>';
24722         } else {
24723             Roo.each(this.stylesheets, function(s) {
24724                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24725             });
24726             
24727         }
24728         
24729         st +=  '<style type="text/css">' +
24730             'IMG { cursor: pointer } ' +
24731         '</style>';
24732
24733         
24734         return '<html><head>' + st  +
24735             //<style type="text/css">' +
24736             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24737             //'</style>' +
24738             ' </head><body class="roo-htmleditor-body"></body></html>';
24739     },
24740
24741     // private
24742     onRender : function(ct, position)
24743     {
24744         var _t = this;
24745         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24746         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24747         
24748         
24749         this.el.dom.style.border = '0 none';
24750         this.el.dom.setAttribute('tabIndex', -1);
24751         this.el.addClass('x-hidden hide');
24752         
24753         
24754         
24755         if(Roo.isIE){ // fix IE 1px bogus margin
24756             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24757         }
24758        
24759         
24760         this.frameId = Roo.id();
24761         
24762          
24763         
24764         var iframe = this.owner.wrap.createChild({
24765             tag: 'iframe',
24766             cls: 'form-control', // bootstrap..
24767             id: this.frameId,
24768             name: this.frameId,
24769             frameBorder : 'no',
24770             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24771         }, this.el
24772         );
24773         
24774         
24775         this.iframe = iframe.dom;
24776
24777          this.assignDocWin();
24778         
24779         this.doc.designMode = 'on';
24780        
24781         this.doc.open();
24782         this.doc.write(this.getDocMarkup());
24783         this.doc.close();
24784
24785         
24786         var task = { // must defer to wait for browser to be ready
24787             run : function(){
24788                 //console.log("run task?" + this.doc.readyState);
24789                 this.assignDocWin();
24790                 if(this.doc.body || this.doc.readyState == 'complete'){
24791                     try {
24792                         this.doc.designMode="on";
24793                     } catch (e) {
24794                         return;
24795                     }
24796                     Roo.TaskMgr.stop(task);
24797                     this.initEditor.defer(10, this);
24798                 }
24799             },
24800             interval : 10,
24801             duration: 10000,
24802             scope: this
24803         };
24804         Roo.TaskMgr.start(task);
24805
24806         
24807          
24808     },
24809
24810     // private
24811     onResize : function(w, h)
24812     {
24813          Roo.log('resize: ' +w + ',' + h );
24814         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24815         if(!this.iframe){
24816             return;
24817         }
24818         if(typeof w == 'number'){
24819             
24820             this.iframe.style.width = w + 'px';
24821         }
24822         if(typeof h == 'number'){
24823             
24824             this.iframe.style.height = h + 'px';
24825             if(this.doc){
24826                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24827             }
24828         }
24829         
24830     },
24831
24832     /**
24833      * Toggles the editor between standard and source edit mode.
24834      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24835      */
24836     toggleSourceEdit : function(sourceEditMode){
24837         
24838         this.sourceEditMode = sourceEditMode === true;
24839         
24840         if(this.sourceEditMode){
24841  
24842             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24843             
24844         }else{
24845             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24846             //this.iframe.className = '';
24847             this.deferFocus();
24848         }
24849         //this.setSize(this.owner.wrap.getSize());
24850         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24851     },
24852
24853     
24854   
24855
24856     /**
24857      * Protected method that will not generally be called directly. If you need/want
24858      * custom HTML cleanup, this is the method you should override.
24859      * @param {String} html The HTML to be cleaned
24860      * return {String} The cleaned HTML
24861      */
24862     cleanHtml : function(html){
24863         html = String(html);
24864         if(html.length > 5){
24865             if(Roo.isSafari){ // strip safari nonsense
24866                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24867             }
24868         }
24869         if(html == '&nbsp;'){
24870             html = '';
24871         }
24872         return html;
24873     },
24874
24875     /**
24876      * HTML Editor -> Textarea
24877      * Protected method that will not generally be called directly. Syncs the contents
24878      * of the editor iframe with the textarea.
24879      */
24880     syncValue : function(){
24881         if(this.initialized){
24882             var bd = (this.doc.body || this.doc.documentElement);
24883             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24884             var html = bd.innerHTML;
24885             if(Roo.isSafari){
24886                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24887                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24888                 if(m && m[1]){
24889                     html = '<div style="'+m[0]+'">' + html + '</div>';
24890                 }
24891             }
24892             html = this.cleanHtml(html);
24893             // fix up the special chars.. normaly like back quotes in word...
24894             // however we do not want to do this with chinese..
24895             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24896                 var cc = b.charCodeAt();
24897                 if (
24898                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24899                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24900                     (cc >= 0xf900 && cc < 0xfb00 )
24901                 ) {
24902                         return b;
24903                 }
24904                 return "&#"+cc+";" 
24905             });
24906             if(this.owner.fireEvent('beforesync', this, html) !== false){
24907                 this.el.dom.value = html;
24908                 this.owner.fireEvent('sync', this, html);
24909             }
24910         }
24911     },
24912
24913     /**
24914      * Protected method that will not generally be called directly. Pushes the value of the textarea
24915      * into the iframe editor.
24916      */
24917     pushValue : function(){
24918         if(this.initialized){
24919             var v = this.el.dom.value.trim();
24920             
24921 //            if(v.length < 1){
24922 //                v = '&#160;';
24923 //            }
24924             
24925             if(this.owner.fireEvent('beforepush', this, v) !== false){
24926                 var d = (this.doc.body || this.doc.documentElement);
24927                 d.innerHTML = v;
24928                 this.cleanUpPaste();
24929                 this.el.dom.value = d.innerHTML;
24930                 this.owner.fireEvent('push', this, v);
24931             }
24932         }
24933     },
24934
24935     // private
24936     deferFocus : function(){
24937         this.focus.defer(10, this);
24938     },
24939
24940     // doc'ed in Field
24941     focus : function(){
24942         if(this.win && !this.sourceEditMode){
24943             this.win.focus();
24944         }else{
24945             this.el.focus();
24946         }
24947     },
24948     
24949     assignDocWin: function()
24950     {
24951         var iframe = this.iframe;
24952         
24953          if(Roo.isIE){
24954             this.doc = iframe.contentWindow.document;
24955             this.win = iframe.contentWindow;
24956         } else {
24957             if (!Roo.get(this.frameId)) {
24958                 return;
24959             }
24960             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24961             this.win = Roo.get(this.frameId).dom.contentWindow;
24962         }
24963     },
24964     
24965     // private
24966     initEditor : function(){
24967         //console.log("INIT EDITOR");
24968         this.assignDocWin();
24969         
24970         
24971         
24972         this.doc.designMode="on";
24973         this.doc.open();
24974         this.doc.write(this.getDocMarkup());
24975         this.doc.close();
24976         
24977         var dbody = (this.doc.body || this.doc.documentElement);
24978         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24979         // this copies styles from the containing element into thsi one..
24980         // not sure why we need all of this..
24981         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24982         
24983         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24984         //ss['background-attachment'] = 'fixed'; // w3c
24985         dbody.bgProperties = 'fixed'; // ie
24986         //Roo.DomHelper.applyStyles(dbody, ss);
24987         Roo.EventManager.on(this.doc, {
24988             //'mousedown': this.onEditorEvent,
24989             'mouseup': this.onEditorEvent,
24990             'dblclick': this.onEditorEvent,
24991             'click': this.onEditorEvent,
24992             'keyup': this.onEditorEvent,
24993             buffer:100,
24994             scope: this
24995         });
24996         if(Roo.isGecko){
24997             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24998         }
24999         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25000             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25001         }
25002         this.initialized = true;
25003
25004         this.owner.fireEvent('initialize', this);
25005         this.pushValue();
25006     },
25007
25008     // private
25009     onDestroy : function(){
25010         
25011         
25012         
25013         if(this.rendered){
25014             
25015             //for (var i =0; i < this.toolbars.length;i++) {
25016             //    // fixme - ask toolbars for heights?
25017             //    this.toolbars[i].onDestroy();
25018            // }
25019             
25020             //this.wrap.dom.innerHTML = '';
25021             //this.wrap.remove();
25022         }
25023     },
25024
25025     // private
25026     onFirstFocus : function(){
25027         
25028         this.assignDocWin();
25029         
25030         
25031         this.activated = true;
25032          
25033     
25034         if(Roo.isGecko){ // prevent silly gecko errors
25035             this.win.focus();
25036             var s = this.win.getSelection();
25037             if(!s.focusNode || s.focusNode.nodeType != 3){
25038                 var r = s.getRangeAt(0);
25039                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25040                 r.collapse(true);
25041                 this.deferFocus();
25042             }
25043             try{
25044                 this.execCmd('useCSS', true);
25045                 this.execCmd('styleWithCSS', false);
25046             }catch(e){}
25047         }
25048         this.owner.fireEvent('activate', this);
25049     },
25050
25051     // private
25052     adjustFont: function(btn){
25053         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25054         //if(Roo.isSafari){ // safari
25055         //    adjust *= 2;
25056        // }
25057         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25058         if(Roo.isSafari){ // safari
25059             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25060             v =  (v < 10) ? 10 : v;
25061             v =  (v > 48) ? 48 : v;
25062             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25063             
25064         }
25065         
25066         
25067         v = Math.max(1, v+adjust);
25068         
25069         this.execCmd('FontSize', v  );
25070     },
25071
25072     onEditorEvent : function(e){
25073         this.owner.fireEvent('editorevent', this, e);
25074       //  this.updateToolbar();
25075         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25076     },
25077
25078     insertTag : function(tg)
25079     {
25080         // could be a bit smarter... -> wrap the current selected tRoo..
25081         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25082             
25083             range = this.createRange(this.getSelection());
25084             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25085             wrappingNode.appendChild(range.extractContents());
25086             range.insertNode(wrappingNode);
25087
25088             return;
25089             
25090             
25091             
25092         }
25093         this.execCmd("formatblock",   tg);
25094         
25095     },
25096     
25097     insertText : function(txt)
25098     {
25099         
25100         
25101         var range = this.createRange();
25102         range.deleteContents();
25103                //alert(Sender.getAttribute('label'));
25104                
25105         range.insertNode(this.doc.createTextNode(txt));
25106     } ,
25107     
25108      
25109
25110     /**
25111      * Executes a Midas editor command on the editor document and performs necessary focus and
25112      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25113      * @param {String} cmd The Midas command
25114      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25115      */
25116     relayCmd : function(cmd, value){
25117         this.win.focus();
25118         this.execCmd(cmd, value);
25119         this.owner.fireEvent('editorevent', this);
25120         //this.updateToolbar();
25121         this.owner.deferFocus();
25122     },
25123
25124     /**
25125      * Executes a Midas editor command directly on the editor document.
25126      * For visual commands, you should use {@link #relayCmd} instead.
25127      * <b>This should only be called after the editor is initialized.</b>
25128      * @param {String} cmd The Midas command
25129      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25130      */
25131     execCmd : function(cmd, value){
25132         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25133         this.syncValue();
25134     },
25135  
25136  
25137    
25138     /**
25139      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25140      * to insert tRoo.
25141      * @param {String} text | dom node.. 
25142      */
25143     insertAtCursor : function(text)
25144     {
25145         
25146         
25147         
25148         if(!this.activated){
25149             return;
25150         }
25151         /*
25152         if(Roo.isIE){
25153             this.win.focus();
25154             var r = this.doc.selection.createRange();
25155             if(r){
25156                 r.collapse(true);
25157                 r.pasteHTML(text);
25158                 this.syncValue();
25159                 this.deferFocus();
25160             
25161             }
25162             return;
25163         }
25164         */
25165         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25166             this.win.focus();
25167             
25168             
25169             // from jquery ui (MIT licenced)
25170             var range, node;
25171             var win = this.win;
25172             
25173             if (win.getSelection && win.getSelection().getRangeAt) {
25174                 range = win.getSelection().getRangeAt(0);
25175                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25176                 range.insertNode(node);
25177             } else if (win.document.selection && win.document.selection.createRange) {
25178                 // no firefox support
25179                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25180                 win.document.selection.createRange().pasteHTML(txt);
25181             } else {
25182                 // no firefox support
25183                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25184                 this.execCmd('InsertHTML', txt);
25185             } 
25186             
25187             this.syncValue();
25188             
25189             this.deferFocus();
25190         }
25191     },
25192  // private
25193     mozKeyPress : function(e){
25194         if(e.ctrlKey){
25195             var c = e.getCharCode(), cmd;
25196           
25197             if(c > 0){
25198                 c = String.fromCharCode(c).toLowerCase();
25199                 switch(c){
25200                     case 'b':
25201                         cmd = 'bold';
25202                         break;
25203                     case 'i':
25204                         cmd = 'italic';
25205                         break;
25206                     
25207                     case 'u':
25208                         cmd = 'underline';
25209                         break;
25210                     
25211                     case 'v':
25212                         this.cleanUpPaste.defer(100, this);
25213                         return;
25214                         
25215                 }
25216                 if(cmd){
25217                     this.win.focus();
25218                     this.execCmd(cmd);
25219                     this.deferFocus();
25220                     e.preventDefault();
25221                 }
25222                 
25223             }
25224         }
25225     },
25226
25227     // private
25228     fixKeys : function(){ // load time branching for fastest keydown performance
25229         if(Roo.isIE){
25230             return function(e){
25231                 var k = e.getKey(), r;
25232                 if(k == e.TAB){
25233                     e.stopEvent();
25234                     r = this.doc.selection.createRange();
25235                     if(r){
25236                         r.collapse(true);
25237                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25238                         this.deferFocus();
25239                     }
25240                     return;
25241                 }
25242                 
25243                 if(k == e.ENTER){
25244                     r = this.doc.selection.createRange();
25245                     if(r){
25246                         var target = r.parentElement();
25247                         if(!target || target.tagName.toLowerCase() != 'li'){
25248                             e.stopEvent();
25249                             r.pasteHTML('<br />');
25250                             r.collapse(false);
25251                             r.select();
25252                         }
25253                     }
25254                 }
25255                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25256                     this.cleanUpPaste.defer(100, this);
25257                     return;
25258                 }
25259                 
25260                 
25261             };
25262         }else if(Roo.isOpera){
25263             return function(e){
25264                 var k = e.getKey();
25265                 if(k == e.TAB){
25266                     e.stopEvent();
25267                     this.win.focus();
25268                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25269                     this.deferFocus();
25270                 }
25271                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25272                     this.cleanUpPaste.defer(100, this);
25273                     return;
25274                 }
25275                 
25276             };
25277         }else if(Roo.isSafari){
25278             return function(e){
25279                 var k = e.getKey();
25280                 
25281                 if(k == e.TAB){
25282                     e.stopEvent();
25283                     this.execCmd('InsertText','\t');
25284                     this.deferFocus();
25285                     return;
25286                 }
25287                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25288                     this.cleanUpPaste.defer(100, this);
25289                     return;
25290                 }
25291                 
25292              };
25293         }
25294     }(),
25295     
25296     getAllAncestors: function()
25297     {
25298         var p = this.getSelectedNode();
25299         var a = [];
25300         if (!p) {
25301             a.push(p); // push blank onto stack..
25302             p = this.getParentElement();
25303         }
25304         
25305         
25306         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25307             a.push(p);
25308             p = p.parentNode;
25309         }
25310         a.push(this.doc.body);
25311         return a;
25312     },
25313     lastSel : false,
25314     lastSelNode : false,
25315     
25316     
25317     getSelection : function() 
25318     {
25319         this.assignDocWin();
25320         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25321     },
25322     
25323     getSelectedNode: function() 
25324     {
25325         // this may only work on Gecko!!!
25326         
25327         // should we cache this!!!!
25328         
25329         
25330         
25331          
25332         var range = this.createRange(this.getSelection()).cloneRange();
25333         
25334         if (Roo.isIE) {
25335             var parent = range.parentElement();
25336             while (true) {
25337                 var testRange = range.duplicate();
25338                 testRange.moveToElementText(parent);
25339                 if (testRange.inRange(range)) {
25340                     break;
25341                 }
25342                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25343                     break;
25344                 }
25345                 parent = parent.parentElement;
25346             }
25347             return parent;
25348         }
25349         
25350         // is ancestor a text element.
25351         var ac =  range.commonAncestorContainer;
25352         if (ac.nodeType == 3) {
25353             ac = ac.parentNode;
25354         }
25355         
25356         var ar = ac.childNodes;
25357          
25358         var nodes = [];
25359         var other_nodes = [];
25360         var has_other_nodes = false;
25361         for (var i=0;i<ar.length;i++) {
25362             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25363                 continue;
25364             }
25365             // fullly contained node.
25366             
25367             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25368                 nodes.push(ar[i]);
25369                 continue;
25370             }
25371             
25372             // probably selected..
25373             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25374                 other_nodes.push(ar[i]);
25375                 continue;
25376             }
25377             // outer..
25378             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25379                 continue;
25380             }
25381             
25382             
25383             has_other_nodes = true;
25384         }
25385         if (!nodes.length && other_nodes.length) {
25386             nodes= other_nodes;
25387         }
25388         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25389             return false;
25390         }
25391         
25392         return nodes[0];
25393     },
25394     createRange: function(sel)
25395     {
25396         // this has strange effects when using with 
25397         // top toolbar - not sure if it's a great idea.
25398         //this.editor.contentWindow.focus();
25399         if (typeof sel != "undefined") {
25400             try {
25401                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25402             } catch(e) {
25403                 return this.doc.createRange();
25404             }
25405         } else {
25406             return this.doc.createRange();
25407         }
25408     },
25409     getParentElement: function()
25410     {
25411         
25412         this.assignDocWin();
25413         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25414         
25415         var range = this.createRange(sel);
25416          
25417         try {
25418             var p = range.commonAncestorContainer;
25419             while (p.nodeType == 3) { // text node
25420                 p = p.parentNode;
25421             }
25422             return p;
25423         } catch (e) {
25424             return null;
25425         }
25426     
25427     },
25428     /***
25429      *
25430      * Range intersection.. the hard stuff...
25431      *  '-1' = before
25432      *  '0' = hits..
25433      *  '1' = after.
25434      *         [ -- selected range --- ]
25435      *   [fail]                        [fail]
25436      *
25437      *    basically..
25438      *      if end is before start or  hits it. fail.
25439      *      if start is after end or hits it fail.
25440      *
25441      *   if either hits (but other is outside. - then it's not 
25442      *   
25443      *    
25444      **/
25445     
25446     
25447     // @see http://www.thismuchiknow.co.uk/?p=64.
25448     rangeIntersectsNode : function(range, node)
25449     {
25450         var nodeRange = node.ownerDocument.createRange();
25451         try {
25452             nodeRange.selectNode(node);
25453         } catch (e) {
25454             nodeRange.selectNodeContents(node);
25455         }
25456     
25457         var rangeStartRange = range.cloneRange();
25458         rangeStartRange.collapse(true);
25459     
25460         var rangeEndRange = range.cloneRange();
25461         rangeEndRange.collapse(false);
25462     
25463         var nodeStartRange = nodeRange.cloneRange();
25464         nodeStartRange.collapse(true);
25465     
25466         var nodeEndRange = nodeRange.cloneRange();
25467         nodeEndRange.collapse(false);
25468     
25469         return rangeStartRange.compareBoundaryPoints(
25470                  Range.START_TO_START, nodeEndRange) == -1 &&
25471                rangeEndRange.compareBoundaryPoints(
25472                  Range.START_TO_START, nodeStartRange) == 1;
25473         
25474          
25475     },
25476     rangeCompareNode : function(range, node)
25477     {
25478         var nodeRange = node.ownerDocument.createRange();
25479         try {
25480             nodeRange.selectNode(node);
25481         } catch (e) {
25482             nodeRange.selectNodeContents(node);
25483         }
25484         
25485         
25486         range.collapse(true);
25487     
25488         nodeRange.collapse(true);
25489      
25490         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25491         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25492          
25493         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25494         
25495         var nodeIsBefore   =  ss == 1;
25496         var nodeIsAfter    = ee == -1;
25497         
25498         if (nodeIsBefore && nodeIsAfter)
25499             return 0; // outer
25500         if (!nodeIsBefore && nodeIsAfter)
25501             return 1; //right trailed.
25502         
25503         if (nodeIsBefore && !nodeIsAfter)
25504             return 2;  // left trailed.
25505         // fully contined.
25506         return 3;
25507     },
25508
25509     // private? - in a new class?
25510     cleanUpPaste :  function()
25511     {
25512         // cleans up the whole document..
25513         Roo.log('cleanuppaste');
25514         
25515         this.cleanUpChildren(this.doc.body);
25516         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25517         if (clean != this.doc.body.innerHTML) {
25518             this.doc.body.innerHTML = clean;
25519         }
25520         
25521     },
25522     
25523     cleanWordChars : function(input) {// change the chars to hex code
25524         var he = Roo.HtmlEditorCore;
25525         
25526         var output = input;
25527         Roo.each(he.swapCodes, function(sw) { 
25528             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25529             
25530             output = output.replace(swapper, sw[1]);
25531         });
25532         
25533         return output;
25534     },
25535     
25536     
25537     cleanUpChildren : function (n)
25538     {
25539         if (!n.childNodes.length) {
25540             return;
25541         }
25542         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25543            this.cleanUpChild(n.childNodes[i]);
25544         }
25545     },
25546     
25547     
25548         
25549     
25550     cleanUpChild : function (node)
25551     {
25552         var ed = this;
25553         //console.log(node);
25554         if (node.nodeName == "#text") {
25555             // clean up silly Windows -- stuff?
25556             return; 
25557         }
25558         if (node.nodeName == "#comment") {
25559             node.parentNode.removeChild(node);
25560             // clean up silly Windows -- stuff?
25561             return; 
25562         }
25563         
25564         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
25565             // remove node.
25566             node.parentNode.removeChild(node);
25567             return;
25568             
25569         }
25570         
25571         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25572         
25573         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25574         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25575         
25576         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25577         //    remove_keep_children = true;
25578         //}
25579         
25580         if (remove_keep_children) {
25581             this.cleanUpChildren(node);
25582             // inserts everything just before this node...
25583             while (node.childNodes.length) {
25584                 var cn = node.childNodes[0];
25585                 node.removeChild(cn);
25586                 node.parentNode.insertBefore(cn, node);
25587             }
25588             node.parentNode.removeChild(node);
25589             return;
25590         }
25591         
25592         if (!node.attributes || !node.attributes.length) {
25593             this.cleanUpChildren(node);
25594             return;
25595         }
25596         
25597         function cleanAttr(n,v)
25598         {
25599             
25600             if (v.match(/^\./) || v.match(/^\//)) {
25601                 return;
25602             }
25603             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25604                 return;
25605             }
25606             if (v.match(/^#/)) {
25607                 return;
25608             }
25609 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25610             node.removeAttribute(n);
25611             
25612         }
25613         
25614         function cleanStyle(n,v)
25615         {
25616             if (v.match(/expression/)) { //XSS?? should we even bother..
25617                 node.removeAttribute(n);
25618                 return;
25619             }
25620             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
25621             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
25622             
25623             
25624             var parts = v.split(/;/);
25625             var clean = [];
25626             
25627             Roo.each(parts, function(p) {
25628                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25629                 if (!p.length) {
25630                     return true;
25631                 }
25632                 var l = p.split(':').shift().replace(/\s+/g,'');
25633                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25634                 
25635                 if ( cblack.indexOf(l) > -1) {
25636 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25637                     //node.removeAttribute(n);
25638                     return true;
25639                 }
25640                 //Roo.log()
25641                 // only allow 'c whitelisted system attributes'
25642                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25643 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25644                     //node.removeAttribute(n);
25645                     return true;
25646                 }
25647                 
25648                 
25649                  
25650                 
25651                 clean.push(p);
25652                 return true;
25653             });
25654             if (clean.length) { 
25655                 node.setAttribute(n, clean.join(';'));
25656             } else {
25657                 node.removeAttribute(n);
25658             }
25659             
25660         }
25661         
25662         
25663         for (var i = node.attributes.length-1; i > -1 ; i--) {
25664             var a = node.attributes[i];
25665             //console.log(a);
25666             
25667             if (a.name.toLowerCase().substr(0,2)=='on')  {
25668                 node.removeAttribute(a.name);
25669                 continue;
25670             }
25671             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25672                 node.removeAttribute(a.name);
25673                 continue;
25674             }
25675             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25676                 cleanAttr(a.name,a.value); // fixme..
25677                 continue;
25678             }
25679             if (a.name == 'style') {
25680                 cleanStyle(a.name,a.value);
25681                 continue;
25682             }
25683             /// clean up MS crap..
25684             // tecnically this should be a list of valid class'es..
25685             
25686             
25687             if (a.name == 'class') {
25688                 if (a.value.match(/^Mso/)) {
25689                     node.className = '';
25690                 }
25691                 
25692                 if (a.value.match(/body/)) {
25693                     node.className = '';
25694                 }
25695                 continue;
25696             }
25697             
25698             // style cleanup!?
25699             // class cleanup?
25700             
25701         }
25702         
25703         
25704         this.cleanUpChildren(node);
25705         
25706         
25707     },
25708     /**
25709      * Clean up MS wordisms...
25710      */
25711     cleanWord : function(node)
25712     {
25713         var _t = this;
25714         var cleanWordChildren = function()
25715         {
25716             if (!node.childNodes.length) {
25717                 return;
25718             }
25719             for (var i = node.childNodes.length-1; i > -1 ; i--) {
25720                _t.cleanWord(node.childNodes[i]);
25721             }
25722         }
25723         
25724         
25725         if (!node) {
25726             this.cleanWord(this.doc.body);
25727             return;
25728         }
25729         if (node.nodeName == "#text") {
25730             // clean up silly Windows -- stuff?
25731             return; 
25732         }
25733         if (node.nodeName == "#comment") {
25734             node.parentNode.removeChild(node);
25735             // clean up silly Windows -- stuff?
25736             return; 
25737         }
25738         
25739         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25740             node.parentNode.removeChild(node);
25741             return;
25742         }
25743         
25744         // remove - but keep children..
25745         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25746             while (node.childNodes.length) {
25747                 var cn = node.childNodes[0];
25748                 node.removeChild(cn);
25749                 node.parentNode.insertBefore(cn, node);
25750             }
25751             node.parentNode.removeChild(node);
25752             cleanWordChildren();
25753             return;
25754         }
25755         // clean styles
25756         if (node.className.length) {
25757             
25758             var cn = node.className.split(/\W+/);
25759             var cna = [];
25760             Roo.each(cn, function(cls) {
25761                 if (cls.match(/Mso[a-zA-Z]+/)) {
25762                     return;
25763                 }
25764                 cna.push(cls);
25765             });
25766             node.className = cna.length ? cna.join(' ') : '';
25767             if (!cna.length) {
25768                 node.removeAttribute("class");
25769             }
25770         }
25771         
25772         if (node.hasAttribute("lang")) {
25773             node.removeAttribute("lang");
25774         }
25775         
25776         if (node.hasAttribute("style")) {
25777             
25778             var styles = node.getAttribute("style").split(";");
25779             var nstyle = [];
25780             Roo.each(styles, function(s) {
25781                 if (!s.match(/:/)) {
25782                     return;
25783                 }
25784                 var kv = s.split(":");
25785                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25786                     return;
25787                 }
25788                 // what ever is left... we allow.
25789                 nstyle.push(s);
25790             });
25791             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25792             if (!nstyle.length) {
25793                 node.removeAttribute('style');
25794             }
25795         }
25796         
25797         cleanWordChildren();
25798         
25799         
25800     },
25801     domToHTML : function(currentElement, depth, nopadtext) {
25802         
25803             depth = depth || 0;
25804             nopadtext = nopadtext || false;
25805         
25806             if (!currentElement) {
25807                 return this.domToHTML(this.doc.body);
25808             }
25809             
25810             //Roo.log(currentElement);
25811             var j;
25812             var allText = false;
25813             var nodeName = currentElement.nodeName;
25814             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25815             
25816             if  (nodeName == '#text') {
25817                 return currentElement.nodeValue;
25818             }
25819             
25820             
25821             var ret = '';
25822             if (nodeName != 'BODY') {
25823                  
25824                 var i = 0;
25825                 // Prints the node tagName, such as <A>, <IMG>, etc
25826                 if (tagName) {
25827                     var attr = [];
25828                     for(i = 0; i < currentElement.attributes.length;i++) {
25829                         // quoting?
25830                         var aname = currentElement.attributes.item(i).name;
25831                         if (!currentElement.attributes.item(i).value.length) {
25832                             continue;
25833                         }
25834                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25835                     }
25836                     
25837                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25838                 } 
25839                 else {
25840                     
25841                     // eack
25842                 }
25843             } else {
25844                 tagName = false;
25845             }
25846             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25847                 return ret;
25848             }
25849             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25850                 nopadtext = true;
25851             }
25852             
25853             
25854             // Traverse the tree
25855             i = 0;
25856             var currentElementChild = currentElement.childNodes.item(i);
25857             var allText = true;
25858             var innerHTML  = '';
25859             lastnode = '';
25860             while (currentElementChild) {
25861                 // Formatting code (indent the tree so it looks nice on the screen)
25862                 var nopad = nopadtext;
25863                 if (lastnode == 'SPAN') {
25864                     nopad  = true;
25865                 }
25866                 // text
25867                 if  (currentElementChild.nodeName == '#text') {
25868                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25869                     if (!nopad && toadd.length > 80) {
25870                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25871                     }
25872                     innerHTML  += toadd;
25873                     
25874                     i++;
25875                     currentElementChild = currentElement.childNodes.item(i);
25876                     lastNode = '';
25877                     continue;
25878                 }
25879                 allText = false;
25880                 
25881                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25882                     
25883                 // Recursively traverse the tree structure of the child node
25884                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25885                 lastnode = currentElementChild.nodeName;
25886                 i++;
25887                 currentElementChild=currentElement.childNodes.item(i);
25888             }
25889             
25890             ret += innerHTML;
25891             
25892             if (!allText) {
25893                     // The remaining code is mostly for formatting the tree
25894                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25895             }
25896             
25897             
25898             if (tagName) {
25899                 ret+= "</"+tagName+">";
25900             }
25901             return ret;
25902             
25903         }
25904     
25905     // hide stuff that is not compatible
25906     /**
25907      * @event blur
25908      * @hide
25909      */
25910     /**
25911      * @event change
25912      * @hide
25913      */
25914     /**
25915      * @event focus
25916      * @hide
25917      */
25918     /**
25919      * @event specialkey
25920      * @hide
25921      */
25922     /**
25923      * @cfg {String} fieldClass @hide
25924      */
25925     /**
25926      * @cfg {String} focusClass @hide
25927      */
25928     /**
25929      * @cfg {String} autoCreate @hide
25930      */
25931     /**
25932      * @cfg {String} inputType @hide
25933      */
25934     /**
25935      * @cfg {String} invalidClass @hide
25936      */
25937     /**
25938      * @cfg {String} invalidText @hide
25939      */
25940     /**
25941      * @cfg {String} msgFx @hide
25942      */
25943     /**
25944      * @cfg {String} validateOnBlur @hide
25945      */
25946 });
25947
25948 Roo.HtmlEditorCore.white = [
25949         'area', 'br', 'img', 'input', 'hr', 'wbr',
25950         
25951        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25952        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25953        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25954        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25955        'table',   'ul',         'xmp', 
25956        
25957        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25958       'thead',   'tr', 
25959      
25960       'dir', 'menu', 'ol', 'ul', 'dl',
25961        
25962       'embed',  'object'
25963 ];
25964
25965
25966 Roo.HtmlEditorCore.black = [
25967     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25968         'applet', // 
25969         'base',   'basefont', 'bgsound', 'blink',  'body', 
25970         'frame',  'frameset', 'head',    'html',   'ilayer', 
25971         'iframe', 'layer',  'link',     'meta',    'object',   
25972         'script', 'style' ,'title',  'xml' // clean later..
25973 ];
25974 Roo.HtmlEditorCore.clean = [
25975     'script', 'style', 'title', 'xml'
25976 ];
25977 Roo.HtmlEditorCore.remove = [
25978     'font'
25979 ];
25980 // attributes..
25981
25982 Roo.HtmlEditorCore.ablack = [
25983     'on'
25984 ];
25985     
25986 Roo.HtmlEditorCore.aclean = [ 
25987     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25988 ];
25989
25990 // protocols..
25991 Roo.HtmlEditorCore.pwhite= [
25992         'http',  'https',  'mailto'
25993 ];
25994
25995 // white listed style attributes.
25996 Roo.HtmlEditorCore.cwhite= [
25997       //  'text-align', /// default is to allow most things..
25998       
25999          
26000 //        'font-size'//??
26001 ];
26002
26003 // black listed style attributes.
26004 Roo.HtmlEditorCore.cblack= [
26005       //  'font-size' -- this can be set by the project 
26006 ];
26007
26008
26009 Roo.HtmlEditorCore.swapCodes   =[ 
26010     [    8211, "--" ], 
26011     [    8212, "--" ], 
26012     [    8216,  "'" ],  
26013     [    8217, "'" ],  
26014     [    8220, '"' ],  
26015     [    8221, '"' ],  
26016     [    8226, "*" ],  
26017     [    8230, "..." ]
26018 ]; 
26019
26020     //<script type="text/javascript">
26021
26022 /*
26023  * Ext JS Library 1.1.1
26024  * Copyright(c) 2006-2007, Ext JS, LLC.
26025  * Licence LGPL
26026  * 
26027  */
26028  
26029  
26030 Roo.form.HtmlEditor = function(config){
26031     
26032     
26033     
26034     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26035     
26036     if (!this.toolbars) {
26037         this.toolbars = [];
26038     }
26039     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26040     
26041     
26042 };
26043
26044 /**
26045  * @class Roo.form.HtmlEditor
26046  * @extends Roo.form.Field
26047  * Provides a lightweight HTML Editor component.
26048  *
26049  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26050  * 
26051  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26052  * supported by this editor.</b><br/><br/>
26053  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26054  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26055  */
26056 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26057     /**
26058      * @cfg {Boolean} clearUp
26059      */
26060     clearUp : true,
26061       /**
26062      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26063      */
26064     toolbars : false,
26065    
26066      /**
26067      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26068      *                        Roo.resizable.
26069      */
26070     resizable : false,
26071      /**
26072      * @cfg {Number} height (in pixels)
26073      */   
26074     height: 300,
26075    /**
26076      * @cfg {Number} width (in pixels)
26077      */   
26078     width: 500,
26079     
26080     /**
26081      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26082      * 
26083      */
26084     stylesheets: false,
26085     
26086     // id of frame..
26087     frameId: false,
26088     
26089     // private properties
26090     validationEvent : false,
26091     deferHeight: true,
26092     initialized : false,
26093     activated : false,
26094     
26095     onFocus : Roo.emptyFn,
26096     iframePad:3,
26097     hideMode:'offsets',
26098     
26099     defaultAutoCreate : { // modified by initCompnoent..
26100         tag: "textarea",
26101         style:"width:500px;height:300px;",
26102         autocomplete: "off"
26103     },
26104
26105     // private
26106     initComponent : function(){
26107         this.addEvents({
26108             /**
26109              * @event initialize
26110              * Fires when the editor is fully initialized (including the iframe)
26111              * @param {HtmlEditor} this
26112              */
26113             initialize: true,
26114             /**
26115              * @event activate
26116              * Fires when the editor is first receives the focus. Any insertion must wait
26117              * until after this event.
26118              * @param {HtmlEditor} this
26119              */
26120             activate: true,
26121              /**
26122              * @event beforesync
26123              * Fires before the textarea is updated with content from the editor iframe. Return false
26124              * to cancel the sync.
26125              * @param {HtmlEditor} this
26126              * @param {String} html
26127              */
26128             beforesync: true,
26129              /**
26130              * @event beforepush
26131              * Fires before the iframe editor is updated with content from the textarea. Return false
26132              * to cancel the push.
26133              * @param {HtmlEditor} this
26134              * @param {String} html
26135              */
26136             beforepush: true,
26137              /**
26138              * @event sync
26139              * Fires when the textarea is updated with content from the editor iframe.
26140              * @param {HtmlEditor} this
26141              * @param {String} html
26142              */
26143             sync: true,
26144              /**
26145              * @event push
26146              * Fires when the iframe editor is updated with content from the textarea.
26147              * @param {HtmlEditor} this
26148              * @param {String} html
26149              */
26150             push: true,
26151              /**
26152              * @event editmodechange
26153              * Fires when the editor switches edit modes
26154              * @param {HtmlEditor} this
26155              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26156              */
26157             editmodechange: true,
26158             /**
26159              * @event editorevent
26160              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26161              * @param {HtmlEditor} this
26162              */
26163             editorevent: true,
26164             /**
26165              * @event firstfocus
26166              * Fires when on first focus - needed by toolbars..
26167              * @param {HtmlEditor} this
26168              */
26169             firstfocus: true,
26170             /**
26171              * @event autosave
26172              * Auto save the htmlEditor value as a file into Events
26173              * @param {HtmlEditor} this
26174              */
26175             autosave: true,
26176             /**
26177              * @event savedpreview
26178              * preview the saved version of htmlEditor
26179              * @param {HtmlEditor} this
26180              */
26181             savedpreview: true
26182         });
26183         this.defaultAutoCreate =  {
26184             tag: "textarea",
26185             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26186             autocomplete: "off"
26187         };
26188     },
26189
26190     /**
26191      * Protected method that will not generally be called directly. It
26192      * is called when the editor creates its toolbar. Override this method if you need to
26193      * add custom toolbar buttons.
26194      * @param {HtmlEditor} editor
26195      */
26196     createToolbar : function(editor){
26197         Roo.log("create toolbars");
26198         if (!editor.toolbars || !editor.toolbars.length) {
26199             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26200         }
26201         
26202         for (var i =0 ; i < editor.toolbars.length;i++) {
26203             editor.toolbars[i] = Roo.factory(
26204                     typeof(editor.toolbars[i]) == 'string' ?
26205                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26206                 Roo.form.HtmlEditor);
26207             editor.toolbars[i].init(editor);
26208         }
26209          
26210         
26211     },
26212
26213      
26214     // private
26215     onRender : function(ct, position)
26216     {
26217         var _t = this;
26218         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26219         
26220         this.wrap = this.el.wrap({
26221             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26222         });
26223         
26224         this.editorcore.onRender(ct, position);
26225          
26226         if (this.resizable) {
26227             this.resizeEl = new Roo.Resizable(this.wrap, {
26228                 pinned : true,
26229                 wrap: true,
26230                 dynamic : true,
26231                 minHeight : this.height,
26232                 height: this.height,
26233                 handles : this.resizable,
26234                 width: this.width,
26235                 listeners : {
26236                     resize : function(r, w, h) {
26237                         _t.onResize(w,h); // -something
26238                     }
26239                 }
26240             });
26241             
26242         }
26243         this.createToolbar(this);
26244        
26245         
26246         if(!this.width){
26247             this.setSize(this.wrap.getSize());
26248         }
26249         if (this.resizeEl) {
26250             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26251             // should trigger onReize..
26252         }
26253         
26254 //        if(this.autosave && this.w){
26255 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26256 //        }
26257     },
26258
26259     // private
26260     onResize : function(w, h)
26261     {
26262         //Roo.log('resize: ' +w + ',' + h );
26263         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26264         var ew = false;
26265         var eh = false;
26266         
26267         if(this.el ){
26268             if(typeof w == 'number'){
26269                 var aw = w - this.wrap.getFrameWidth('lr');
26270                 this.el.setWidth(this.adjustWidth('textarea', aw));
26271                 ew = aw;
26272             }
26273             if(typeof h == 'number'){
26274                 var tbh = 0;
26275                 for (var i =0; i < this.toolbars.length;i++) {
26276                     // fixme - ask toolbars for heights?
26277                     tbh += this.toolbars[i].tb.el.getHeight();
26278                     if (this.toolbars[i].footer) {
26279                         tbh += this.toolbars[i].footer.el.getHeight();
26280                     }
26281                 }
26282                 
26283                 
26284                 
26285                 
26286                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26287                 ah -= 5; // knock a few pixes off for look..
26288                 this.el.setHeight(this.adjustWidth('textarea', ah));
26289                 var eh = ah;
26290             }
26291         }
26292         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26293         this.editorcore.onResize(ew,eh);
26294         
26295     },
26296
26297     /**
26298      * Toggles the editor between standard and source edit mode.
26299      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26300      */
26301     toggleSourceEdit : function(sourceEditMode)
26302     {
26303         this.editorcore.toggleSourceEdit(sourceEditMode);
26304         
26305         if(this.editorcore.sourceEditMode){
26306             Roo.log('editor - showing textarea');
26307             
26308 //            Roo.log('in');
26309 //            Roo.log(this.syncValue());
26310             this.editorcore.syncValue();
26311             this.el.removeClass('x-hidden');
26312             this.el.dom.removeAttribute('tabIndex');
26313             this.el.focus();
26314         }else{
26315             Roo.log('editor - hiding textarea');
26316 //            Roo.log('out')
26317 //            Roo.log(this.pushValue()); 
26318             this.editorcore.pushValue();
26319             
26320             this.el.addClass('x-hidden');
26321             this.el.dom.setAttribute('tabIndex', -1);
26322             //this.deferFocus();
26323         }
26324          
26325         this.setSize(this.wrap.getSize());
26326         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26327     },
26328  
26329     // private (for BoxComponent)
26330     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26331
26332     // private (for BoxComponent)
26333     getResizeEl : function(){
26334         return this.wrap;
26335     },
26336
26337     // private (for BoxComponent)
26338     getPositionEl : function(){
26339         return this.wrap;
26340     },
26341
26342     // private
26343     initEvents : function(){
26344         this.originalValue = this.getValue();
26345     },
26346
26347     /**
26348      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26349      * @method
26350      */
26351     markInvalid : Roo.emptyFn,
26352     /**
26353      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26354      * @method
26355      */
26356     clearInvalid : Roo.emptyFn,
26357
26358     setValue : function(v){
26359         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26360         this.editorcore.pushValue();
26361     },
26362
26363      
26364     // private
26365     deferFocus : function(){
26366         this.focus.defer(10, this);
26367     },
26368
26369     // doc'ed in Field
26370     focus : function(){
26371         this.editorcore.focus();
26372         
26373     },
26374       
26375
26376     // private
26377     onDestroy : function(){
26378         
26379         
26380         
26381         if(this.rendered){
26382             
26383             for (var i =0; i < this.toolbars.length;i++) {
26384                 // fixme - ask toolbars for heights?
26385                 this.toolbars[i].onDestroy();
26386             }
26387             
26388             this.wrap.dom.innerHTML = '';
26389             this.wrap.remove();
26390         }
26391     },
26392
26393     // private
26394     onFirstFocus : function(){
26395         //Roo.log("onFirstFocus");
26396         this.editorcore.onFirstFocus();
26397          for (var i =0; i < this.toolbars.length;i++) {
26398             this.toolbars[i].onFirstFocus();
26399         }
26400         
26401     },
26402     
26403     // private
26404     syncValue : function()
26405     {
26406         this.editorcore.syncValue();
26407     },
26408     
26409     pushValue : function()
26410     {
26411         this.editorcore.pushValue();
26412     }
26413      
26414     
26415     // hide stuff that is not compatible
26416     /**
26417      * @event blur
26418      * @hide
26419      */
26420     /**
26421      * @event change
26422      * @hide
26423      */
26424     /**
26425      * @event focus
26426      * @hide
26427      */
26428     /**
26429      * @event specialkey
26430      * @hide
26431      */
26432     /**
26433      * @cfg {String} fieldClass @hide
26434      */
26435     /**
26436      * @cfg {String} focusClass @hide
26437      */
26438     /**
26439      * @cfg {String} autoCreate @hide
26440      */
26441     /**
26442      * @cfg {String} inputType @hide
26443      */
26444     /**
26445      * @cfg {String} invalidClass @hide
26446      */
26447     /**
26448      * @cfg {String} invalidText @hide
26449      */
26450     /**
26451      * @cfg {String} msgFx @hide
26452      */
26453     /**
26454      * @cfg {String} validateOnBlur @hide
26455      */
26456 });
26457  
26458     // <script type="text/javascript">
26459 /*
26460  * Based on
26461  * Ext JS Library 1.1.1
26462  * Copyright(c) 2006-2007, Ext JS, LLC.
26463  *  
26464  
26465  */
26466
26467 /**
26468  * @class Roo.form.HtmlEditorToolbar1
26469  * Basic Toolbar
26470  * 
26471  * Usage:
26472  *
26473  new Roo.form.HtmlEditor({
26474     ....
26475     toolbars : [
26476         new Roo.form.HtmlEditorToolbar1({
26477             disable : { fonts: 1 , format: 1, ..., ... , ...],
26478             btns : [ .... ]
26479         })
26480     }
26481      
26482  * 
26483  * @cfg {Object} disable List of elements to disable..
26484  * @cfg {Array} btns List of additional buttons.
26485  * 
26486  * 
26487  * NEEDS Extra CSS? 
26488  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26489  */
26490  
26491 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26492 {
26493     
26494     Roo.apply(this, config);
26495     
26496     // default disabled, based on 'good practice'..
26497     this.disable = this.disable || {};
26498     Roo.applyIf(this.disable, {
26499         fontSize : true,
26500         colors : true,
26501         specialElements : true
26502     });
26503     
26504     
26505     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26506     // dont call parent... till later.
26507 }
26508
26509 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26510     
26511     tb: false,
26512     
26513     rendered: false,
26514     
26515     editor : false,
26516     editorcore : false,
26517     /**
26518      * @cfg {Object} disable  List of toolbar elements to disable
26519          
26520      */
26521     disable : false,
26522     
26523     
26524      /**
26525      * @cfg {String} createLinkText The default text for the create link prompt
26526      */
26527     createLinkText : 'Please enter the URL for the link:',
26528     /**
26529      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
26530      */
26531     defaultLinkValue : 'http:/'+'/',
26532    
26533     
26534       /**
26535      * @cfg {Array} fontFamilies An array of available font families
26536      */
26537     fontFamilies : [
26538         'Arial',
26539         'Courier New',
26540         'Tahoma',
26541         'Times New Roman',
26542         'Verdana'
26543     ],
26544     
26545     specialChars : [
26546            "&#169;",
26547           "&#174;",     
26548           "&#8482;",    
26549           "&#163;" ,    
26550          // "&#8212;",    
26551           "&#8230;",    
26552           "&#247;" ,    
26553         //  "&#225;" ,     ?? a acute?
26554            "&#8364;"    , //Euro
26555        //   "&#8220;"    ,
26556         //  "&#8221;"    ,
26557         //  "&#8226;"    ,
26558           "&#176;"  //   , // degrees
26559
26560          // "&#233;"     , // e ecute
26561          // "&#250;"     , // u ecute?
26562     ],
26563     
26564     specialElements : [
26565         {
26566             text: "Insert Table",
26567             xtype: 'MenuItem',
26568             xns : Roo.Menu,
26569             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26570                 
26571         },
26572         {    
26573             text: "Insert Image",
26574             xtype: 'MenuItem',
26575             xns : Roo.Menu,
26576             ihtml : '<img src="about:blank"/>'
26577             
26578         }
26579         
26580          
26581     ],
26582     
26583     
26584     inputElements : [ 
26585             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26586             "input:submit", "input:button", "select", "textarea", "label" ],
26587     formats : [
26588         ["p"] ,  
26589         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26590         ["pre"],[ "code"], 
26591         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26592         ['div'],['span']
26593     ],
26594     
26595     cleanStyles : [
26596         "font-size"
26597     ],
26598      /**
26599      * @cfg {String} defaultFont default font to use.
26600      */
26601     defaultFont: 'tahoma',
26602    
26603     fontSelect : false,
26604     
26605     
26606     formatCombo : false,
26607     
26608     init : function(editor)
26609     {
26610         this.editor = editor;
26611         this.editorcore = editor.editorcore ? editor.editorcore : editor;
26612         var editorcore = this.editorcore;
26613         
26614         var _t = this;
26615         
26616         var fid = editorcore.frameId;
26617         var etb = this;
26618         function btn(id, toggle, handler){
26619             var xid = fid + '-'+ id ;
26620             return {
26621                 id : xid,
26622                 cmd : id,
26623                 cls : 'x-btn-icon x-edit-'+id,
26624                 enableToggle:toggle !== false,
26625                 scope: _t, // was editor...
26626                 handler:handler||_t.relayBtnCmd,
26627                 clickEvent:'mousedown',
26628                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26629                 tabIndex:-1
26630             };
26631         }
26632         
26633         
26634         
26635         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26636         this.tb = tb;
26637          // stop form submits
26638         tb.el.on('click', function(e){
26639             e.preventDefault(); // what does this do?
26640         });
26641
26642         if(!this.disable.font) { // && !Roo.isSafari){
26643             /* why no safari for fonts 
26644             editor.fontSelect = tb.el.createChild({
26645                 tag:'select',
26646                 tabIndex: -1,
26647                 cls:'x-font-select',
26648                 html: this.createFontOptions()
26649             });
26650             
26651             editor.fontSelect.on('change', function(){
26652                 var font = editor.fontSelect.dom.value;
26653                 editor.relayCmd('fontname', font);
26654                 editor.deferFocus();
26655             }, editor);
26656             
26657             tb.add(
26658                 editor.fontSelect.dom,
26659                 '-'
26660             );
26661             */
26662             
26663         };
26664         if(!this.disable.formats){
26665             this.formatCombo = new Roo.form.ComboBox({
26666                 store: new Roo.data.SimpleStore({
26667                     id : 'tag',
26668                     fields: ['tag'],
26669                     data : this.formats // from states.js
26670                 }),
26671                 blockFocus : true,
26672                 name : '',
26673                 //autoCreate : {tag: "div",  size: "20"},
26674                 displayField:'tag',
26675                 typeAhead: false,
26676                 mode: 'local',
26677                 editable : false,
26678                 triggerAction: 'all',
26679                 emptyText:'Add tag',
26680                 selectOnFocus:true,
26681                 width:135,
26682                 listeners : {
26683                     'select': function(c, r, i) {
26684                         editorcore.insertTag(r.get('tag'));
26685                         editor.focus();
26686                     }
26687                 }
26688
26689             });
26690             tb.addField(this.formatCombo);
26691             
26692         }
26693         
26694         if(!this.disable.format){
26695             tb.add(
26696                 btn('bold'),
26697                 btn('italic'),
26698                 btn('underline')
26699             );
26700         };
26701         if(!this.disable.fontSize){
26702             tb.add(
26703                 '-',
26704                 
26705                 
26706                 btn('increasefontsize', false, editorcore.adjustFont),
26707                 btn('decreasefontsize', false, editorcore.adjustFont)
26708             );
26709         };
26710         
26711         
26712         if(!this.disable.colors){
26713             tb.add(
26714                 '-', {
26715                     id:editorcore.frameId +'-forecolor',
26716                     cls:'x-btn-icon x-edit-forecolor',
26717                     clickEvent:'mousedown',
26718                     tooltip: this.buttonTips['forecolor'] || undefined,
26719                     tabIndex:-1,
26720                     menu : new Roo.menu.ColorMenu({
26721                         allowReselect: true,
26722                         focus: Roo.emptyFn,
26723                         value:'000000',
26724                         plain:true,
26725                         selectHandler: function(cp, color){
26726                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26727                             editor.deferFocus();
26728                         },
26729                         scope: editorcore,
26730                         clickEvent:'mousedown'
26731                     })
26732                 }, {
26733                     id:editorcore.frameId +'backcolor',
26734                     cls:'x-btn-icon x-edit-backcolor',
26735                     clickEvent:'mousedown',
26736                     tooltip: this.buttonTips['backcolor'] || undefined,
26737                     tabIndex:-1,
26738                     menu : new Roo.menu.ColorMenu({
26739                         focus: Roo.emptyFn,
26740                         value:'FFFFFF',
26741                         plain:true,
26742                         allowReselect: true,
26743                         selectHandler: function(cp, color){
26744                             if(Roo.isGecko){
26745                                 editorcore.execCmd('useCSS', false);
26746                                 editorcore.execCmd('hilitecolor', color);
26747                                 editorcore.execCmd('useCSS', true);
26748                                 editor.deferFocus();
26749                             }else{
26750                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26751                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26752                                 editor.deferFocus();
26753                             }
26754                         },
26755                         scope:editorcore,
26756                         clickEvent:'mousedown'
26757                     })
26758                 }
26759             );
26760         };
26761         // now add all the items...
26762         
26763
26764         if(!this.disable.alignments){
26765             tb.add(
26766                 '-',
26767                 btn('justifyleft'),
26768                 btn('justifycenter'),
26769                 btn('justifyright')
26770             );
26771         };
26772
26773         //if(!Roo.isSafari){
26774             if(!this.disable.links){
26775                 tb.add(
26776                     '-',
26777                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
26778                 );
26779             };
26780
26781             if(!this.disable.lists){
26782                 tb.add(
26783                     '-',
26784                     btn('insertorderedlist'),
26785                     btn('insertunorderedlist')
26786                 );
26787             }
26788             if(!this.disable.sourceEdit){
26789                 tb.add(
26790                     '-',
26791                     btn('sourceedit', true, function(btn){
26792                         Roo.log(this);
26793                         this.toggleSourceEdit(btn.pressed);
26794                     })
26795                 );
26796             }
26797         //}
26798         
26799         var smenu = { };
26800         // special menu.. - needs to be tidied up..
26801         if (!this.disable.special) {
26802             smenu = {
26803                 text: "&#169;",
26804                 cls: 'x-edit-none',
26805                 
26806                 menu : {
26807                     items : []
26808                 }
26809             };
26810             for (var i =0; i < this.specialChars.length; i++) {
26811                 smenu.menu.items.push({
26812                     
26813                     html: this.specialChars[i],
26814                     handler: function(a,b) {
26815                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26816                         //editor.insertAtCursor(a.html);
26817                         
26818                     },
26819                     tabIndex:-1
26820                 });
26821             }
26822             
26823             
26824             tb.add(smenu);
26825             
26826             
26827         }
26828         
26829         var cmenu = { };
26830         if (!this.disable.cleanStyles) {
26831             cmenu = {
26832                 cls: 'x-btn-icon x-btn-clear',
26833                 
26834                 menu : {
26835                     items : []
26836                 }
26837             };
26838             for (var i =0; i < this.cleanStyles.length; i++) {
26839                 cmenu.menu.items.push({
26840                     actiontype : this.cleanStyles[i],
26841                     html: 'Remove ' + this.cleanStyles[i],
26842                     handler: function(a,b) {
26843                         Roo.log(a);
26844                         Roo.log(b);
26845                         var c = Roo.get(editorcore.doc.body);
26846                         c.select('[style]').each(function(s) {
26847                             s.dom.style.removeProperty(a.actiontype);
26848                         });
26849                         editorcore.syncValue();
26850                     },
26851                     tabIndex:-1
26852                 });
26853             }
26854             cmenu.menu.items.push({
26855                 actiontype : 'word',
26856                 html: 'Remove MS Word Formating',
26857                 handler: function(a,b) {
26858                     editorcore.cleanWord();
26859                     editorcore.syncValue();
26860                 },
26861                 tabIndex:-1
26862             });
26863             
26864             cmenu.menu.items.push({
26865                 actiontype : 'all',
26866                 html: 'Remove All Styles',
26867                 handler: function(a,b) {
26868                     
26869                     var c = Roo.get(editorcore.doc.body);
26870                     c.select('[style]').each(function(s) {
26871                         s.dom.removeAttribute('style');
26872                     });
26873                     editorcore.syncValue();
26874                 },
26875                 tabIndex:-1
26876             });
26877              cmenu.menu.items.push({
26878                 actiontype : 'word',
26879                 html: 'Tidy HTML Source',
26880                 handler: function(a,b) {
26881                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
26882                     editorcore.syncValue();
26883                 },
26884                 tabIndex:-1
26885             });
26886             
26887             
26888             tb.add(cmenu);
26889         }
26890          
26891         if (!this.disable.specialElements) {
26892             var semenu = {
26893                 text: "Other;",
26894                 cls: 'x-edit-none',
26895                 menu : {
26896                     items : []
26897                 }
26898             };
26899             for (var i =0; i < this.specialElements.length; i++) {
26900                 semenu.menu.items.push(
26901                     Roo.apply({ 
26902                         handler: function(a,b) {
26903                             editor.insertAtCursor(this.ihtml);
26904                         }
26905                     }, this.specialElements[i])
26906                 );
26907                     
26908             }
26909             
26910             tb.add(semenu);
26911             
26912             
26913         }
26914          
26915         
26916         if (this.btns) {
26917             for(var i =0; i< this.btns.length;i++) {
26918                 var b = Roo.factory(this.btns[i],Roo.form);
26919                 b.cls =  'x-edit-none';
26920                 b.scope = editorcore;
26921                 tb.add(b);
26922             }
26923         
26924         }
26925         
26926         
26927         
26928         // disable everything...
26929         
26930         this.tb.items.each(function(item){
26931            if(item.id != editorcore.frameId+ '-sourceedit'){
26932                 item.disable();
26933             }
26934         });
26935         this.rendered = true;
26936         
26937         // the all the btns;
26938         editor.on('editorevent', this.updateToolbar, this);
26939         // other toolbars need to implement this..
26940         //editor.on('editmodechange', this.updateToolbar, this);
26941     },
26942     
26943     
26944     relayBtnCmd : function(btn) {
26945         this.editorcore.relayCmd(btn.cmd);
26946     },
26947     // private used internally
26948     createLink : function(){
26949         Roo.log("create link?");
26950         var url = prompt(this.createLinkText, this.defaultLinkValue);
26951         if(url && url != 'http:/'+'/'){
26952             this.editorcore.relayCmd('createlink', url);
26953         }
26954     },
26955
26956     
26957     /**
26958      * Protected method that will not generally be called directly. It triggers
26959      * a toolbar update by reading the markup state of the current selection in the editor.
26960      */
26961     updateToolbar: function(){
26962
26963         if(!this.editorcore.activated){
26964             this.editor.onFirstFocus();
26965             return;
26966         }
26967
26968         var btns = this.tb.items.map, 
26969             doc = this.editorcore.doc,
26970             frameId = this.editorcore.frameId;
26971
26972         if(!this.disable.font && !Roo.isSafari){
26973             /*
26974             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26975             if(name != this.fontSelect.dom.value){
26976                 this.fontSelect.dom.value = name;
26977             }
26978             */
26979         }
26980         if(!this.disable.format){
26981             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26982             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26983             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26984         }
26985         if(!this.disable.alignments){
26986             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26987             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26988             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26989         }
26990         if(!Roo.isSafari && !this.disable.lists){
26991             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26992             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26993         }
26994         
26995         var ans = this.editorcore.getAllAncestors();
26996         if (this.formatCombo) {
26997             
26998             
26999             var store = this.formatCombo.store;
27000             this.formatCombo.setValue("");
27001             for (var i =0; i < ans.length;i++) {
27002                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27003                     // select it..
27004                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27005                     break;
27006                 }
27007             }
27008         }
27009         
27010         
27011         
27012         // hides menus... - so this cant be on a menu...
27013         Roo.menu.MenuMgr.hideAll();
27014
27015         //this.editorsyncValue();
27016     },
27017    
27018     
27019     createFontOptions : function(){
27020         var buf = [], fs = this.fontFamilies, ff, lc;
27021         
27022         
27023         
27024         for(var i = 0, len = fs.length; i< len; i++){
27025             ff = fs[i];
27026             lc = ff.toLowerCase();
27027             buf.push(
27028                 '<option value="',lc,'" style="font-family:',ff,';"',
27029                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27030                     ff,
27031                 '</option>'
27032             );
27033         }
27034         return buf.join('');
27035     },
27036     
27037     toggleSourceEdit : function(sourceEditMode){
27038         
27039         Roo.log("toolbar toogle");
27040         if(sourceEditMode === undefined){
27041             sourceEditMode = !this.sourceEditMode;
27042         }
27043         this.sourceEditMode = sourceEditMode === true;
27044         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27045         // just toggle the button?
27046         if(btn.pressed !== this.sourceEditMode){
27047             btn.toggle(this.sourceEditMode);
27048             return;
27049         }
27050         
27051         if(sourceEditMode){
27052             Roo.log("disabling buttons");
27053             this.tb.items.each(function(item){
27054                 if(item.cmd != 'sourceedit'){
27055                     item.disable();
27056                 }
27057             });
27058           
27059         }else{
27060             Roo.log("enabling buttons");
27061             if(this.editorcore.initialized){
27062                 this.tb.items.each(function(item){
27063                     item.enable();
27064                 });
27065             }
27066             
27067         }
27068         Roo.log("calling toggole on editor");
27069         // tell the editor that it's been pressed..
27070         this.editor.toggleSourceEdit(sourceEditMode);
27071        
27072     },
27073      /**
27074      * Object collection of toolbar tooltips for the buttons in the editor. The key
27075      * is the command id associated with that button and the value is a valid QuickTips object.
27076      * For example:
27077 <pre><code>
27078 {
27079     bold : {
27080         title: 'Bold (Ctrl+B)',
27081         text: 'Make the selected text bold.',
27082         cls: 'x-html-editor-tip'
27083     },
27084     italic : {
27085         title: 'Italic (Ctrl+I)',
27086         text: 'Make the selected text italic.',
27087         cls: 'x-html-editor-tip'
27088     },
27089     ...
27090 </code></pre>
27091     * @type Object
27092      */
27093     buttonTips : {
27094         bold : {
27095             title: 'Bold (Ctrl+B)',
27096             text: 'Make the selected text bold.',
27097             cls: 'x-html-editor-tip'
27098         },
27099         italic : {
27100             title: 'Italic (Ctrl+I)',
27101             text: 'Make the selected text italic.',
27102             cls: 'x-html-editor-tip'
27103         },
27104         underline : {
27105             title: 'Underline (Ctrl+U)',
27106             text: 'Underline the selected text.',
27107             cls: 'x-html-editor-tip'
27108         },
27109         increasefontsize : {
27110             title: 'Grow Text',
27111             text: 'Increase the font size.',
27112             cls: 'x-html-editor-tip'
27113         },
27114         decreasefontsize : {
27115             title: 'Shrink Text',
27116             text: 'Decrease the font size.',
27117             cls: 'x-html-editor-tip'
27118         },
27119         backcolor : {
27120             title: 'Text Highlight Color',
27121             text: 'Change the background color of the selected text.',
27122             cls: 'x-html-editor-tip'
27123         },
27124         forecolor : {
27125             title: 'Font Color',
27126             text: 'Change the color of the selected text.',
27127             cls: 'x-html-editor-tip'
27128         },
27129         justifyleft : {
27130             title: 'Align Text Left',
27131             text: 'Align text to the left.',
27132             cls: 'x-html-editor-tip'
27133         },
27134         justifycenter : {
27135             title: 'Center Text',
27136             text: 'Center text in the editor.',
27137             cls: 'x-html-editor-tip'
27138         },
27139         justifyright : {
27140             title: 'Align Text Right',
27141             text: 'Align text to the right.',
27142             cls: 'x-html-editor-tip'
27143         },
27144         insertunorderedlist : {
27145             title: 'Bullet List',
27146             text: 'Start a bulleted list.',
27147             cls: 'x-html-editor-tip'
27148         },
27149         insertorderedlist : {
27150             title: 'Numbered List',
27151             text: 'Start a numbered list.',
27152             cls: 'x-html-editor-tip'
27153         },
27154         createlink : {
27155             title: 'Hyperlink',
27156             text: 'Make the selected text a hyperlink.',
27157             cls: 'x-html-editor-tip'
27158         },
27159         sourceedit : {
27160             title: 'Source Edit',
27161             text: 'Switch to source editing mode.',
27162             cls: 'x-html-editor-tip'
27163         }
27164     },
27165     // private
27166     onDestroy : function(){
27167         if(this.rendered){
27168             
27169             this.tb.items.each(function(item){
27170                 if(item.menu){
27171                     item.menu.removeAll();
27172                     if(item.menu.el){
27173                         item.menu.el.destroy();
27174                     }
27175                 }
27176                 item.destroy();
27177             });
27178              
27179         }
27180     },
27181     onFirstFocus: function() {
27182         this.tb.items.each(function(item){
27183            item.enable();
27184         });
27185     }
27186 });
27187
27188
27189
27190
27191 // <script type="text/javascript">
27192 /*
27193  * Based on
27194  * Ext JS Library 1.1.1
27195  * Copyright(c) 2006-2007, Ext JS, LLC.
27196  *  
27197  
27198  */
27199
27200  
27201 /**
27202  * @class Roo.form.HtmlEditor.ToolbarContext
27203  * Context Toolbar
27204  * 
27205  * Usage:
27206  *
27207  new Roo.form.HtmlEditor({
27208     ....
27209     toolbars : [
27210         { xtype: 'ToolbarStandard', styles : {} }
27211         { xtype: 'ToolbarContext', disable : {} }
27212     ]
27213 })
27214
27215      
27216  * 
27217  * @config : {Object} disable List of elements to disable.. (not done yet.)
27218  * @config : {Object} styles  Map of styles available.
27219  * 
27220  */
27221
27222 Roo.form.HtmlEditor.ToolbarContext = function(config)
27223 {
27224     
27225     Roo.apply(this, config);
27226     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27227     // dont call parent... till later.
27228     this.styles = this.styles || {};
27229 }
27230
27231  
27232
27233 Roo.form.HtmlEditor.ToolbarContext.types = {
27234     'IMG' : {
27235         width : {
27236             title: "Width",
27237             width: 40
27238         },
27239         height:  {
27240             title: "Height",
27241             width: 40
27242         },
27243         align: {
27244             title: "Align",
27245             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27246             width : 80
27247             
27248         },
27249         border: {
27250             title: "Border",
27251             width: 40
27252         },
27253         alt: {
27254             title: "Alt",
27255             width: 120
27256         },
27257         src : {
27258             title: "Src",
27259             width: 220
27260         }
27261         
27262     },
27263     'A' : {
27264         name : {
27265             title: "Name",
27266             width: 50
27267         },
27268         target:  {
27269             title: "Target",
27270             width: 120
27271         },
27272         href:  {
27273             title: "Href",
27274             width: 220
27275         } // border?
27276         
27277     },
27278     'TABLE' : {
27279         rows : {
27280             title: "Rows",
27281             width: 20
27282         },
27283         cols : {
27284             title: "Cols",
27285             width: 20
27286         },
27287         width : {
27288             title: "Width",
27289             width: 40
27290         },
27291         height : {
27292             title: "Height",
27293             width: 40
27294         },
27295         border : {
27296             title: "Border",
27297             width: 20
27298         }
27299     },
27300     'TD' : {
27301         width : {
27302             title: "Width",
27303             width: 40
27304         },
27305         height : {
27306             title: "Height",
27307             width: 40
27308         },   
27309         align: {
27310             title: "Align",
27311             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27312             width: 80
27313         },
27314         valign: {
27315             title: "Valign",
27316             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27317             width: 80
27318         },
27319         colspan: {
27320             title: "Colspan",
27321             width: 20
27322             
27323         },
27324          'font-family'  : {
27325             title : "Font",
27326             style : 'fontFamily',
27327             displayField: 'display',
27328             optname : 'font-family',
27329             width: 140
27330         }
27331     },
27332     'INPUT' : {
27333         name : {
27334             title: "name",
27335             width: 120
27336         },
27337         value : {
27338             title: "Value",
27339             width: 120
27340         },
27341         width : {
27342             title: "Width",
27343             width: 40
27344         }
27345     },
27346     'LABEL' : {
27347         'for' : {
27348             title: "For",
27349             width: 120
27350         }
27351     },
27352     'TEXTAREA' : {
27353           name : {
27354             title: "name",
27355             width: 120
27356         },
27357         rows : {
27358             title: "Rows",
27359             width: 20
27360         },
27361         cols : {
27362             title: "Cols",
27363             width: 20
27364         }
27365     },
27366     'SELECT' : {
27367         name : {
27368             title: "name",
27369             width: 120
27370         },
27371         selectoptions : {
27372             title: "Options",
27373             width: 200
27374         }
27375     },
27376     
27377     // should we really allow this??
27378     // should this just be 
27379     'BODY' : {
27380         title : {
27381             title: "Title",
27382             width: 200,
27383             disabled : true
27384         }
27385     },
27386     'SPAN' : {
27387         'font-family'  : {
27388             title : "Font",
27389             style : 'fontFamily',
27390             displayField: 'display',
27391             optname : 'font-family',
27392             width: 140
27393         }
27394     },
27395     'DIV' : {
27396         'font-family'  : {
27397             title : "Font",
27398             style : 'fontFamily',
27399             displayField: 'display',
27400             optname : 'font-family',
27401             width: 140
27402         }
27403     },
27404      'P' : {
27405         'font-family'  : {
27406             title : "Font",
27407             style : 'fontFamily',
27408             displayField: 'display',
27409             optname : 'font-family',
27410             width: 140
27411         }
27412     },
27413     
27414     '*' : {
27415         // empty..
27416     }
27417
27418 };
27419
27420 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27421 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27422
27423 Roo.form.HtmlEditor.ToolbarContext.options = {
27424         'font-family'  : [ 
27425                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27426                 [ 'Courier New', 'Courier New'],
27427                 [ 'Tahoma', 'Tahoma'],
27428                 [ 'Times New Roman,serif', 'Times'],
27429                 [ 'Verdana','Verdana' ]
27430         ]
27431 };
27432
27433 // fixme - these need to be configurable..
27434  
27435
27436 Roo.form.HtmlEditor.ToolbarContext.types
27437
27438
27439 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27440     
27441     tb: false,
27442     
27443     rendered: false,
27444     
27445     editor : false,
27446     editorcore : false,
27447     /**
27448      * @cfg {Object} disable  List of toolbar elements to disable
27449          
27450      */
27451     disable : false,
27452     /**
27453      * @cfg {Object} styles List of styles 
27454      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27455      *
27456      * These must be defined in the page, so they get rendered correctly..
27457      * .headline { }
27458      * TD.underline { }
27459      * 
27460      */
27461     styles : false,
27462     
27463     options: false,
27464     
27465     toolbars : false,
27466     
27467     init : function(editor)
27468     {
27469         this.editor = editor;
27470         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27471         var editorcore = this.editorcore;
27472         
27473         var fid = editorcore.frameId;
27474         var etb = this;
27475         function btn(id, toggle, handler){
27476             var xid = fid + '-'+ id ;
27477             return {
27478                 id : xid,
27479                 cmd : id,
27480                 cls : 'x-btn-icon x-edit-'+id,
27481                 enableToggle:toggle !== false,
27482                 scope: editorcore, // was editor...
27483                 handler:handler||editorcore.relayBtnCmd,
27484                 clickEvent:'mousedown',
27485                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27486                 tabIndex:-1
27487             };
27488         }
27489         // create a new element.
27490         var wdiv = editor.wrap.createChild({
27491                 tag: 'div'
27492             }, editor.wrap.dom.firstChild.nextSibling, true);
27493         
27494         // can we do this more than once??
27495         
27496          // stop form submits
27497       
27498  
27499         // disable everything...
27500         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27501         this.toolbars = {};
27502            
27503         for (var i in  ty) {
27504           
27505             this.toolbars[i] = this.buildToolbar(ty[i],i);
27506         }
27507         this.tb = this.toolbars.BODY;
27508         this.tb.el.show();
27509         this.buildFooter();
27510         this.footer.show();
27511         editor.on('hide', function( ) { this.footer.hide() }, this);
27512         editor.on('show', function( ) { this.footer.show() }, this);
27513         
27514          
27515         this.rendered = true;
27516         
27517         // the all the btns;
27518         editor.on('editorevent', this.updateToolbar, this);
27519         // other toolbars need to implement this..
27520         //editor.on('editmodechange', this.updateToolbar, this);
27521     },
27522     
27523     
27524     
27525     /**
27526      * Protected method that will not generally be called directly. It triggers
27527      * a toolbar update by reading the markup state of the current selection in the editor.
27528      */
27529     updateToolbar: function(editor,ev,sel){
27530
27531         //Roo.log(ev);
27532         // capture mouse up - this is handy for selecting images..
27533         // perhaps should go somewhere else...
27534         if(!this.editorcore.activated){
27535              this.editor.onFirstFocus();
27536             return;
27537         }
27538         
27539         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27540         // selectNode - might want to handle IE?
27541         if (ev &&
27542             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27543             ev.target && ev.target.tagName == 'IMG') {
27544             // they have click on an image...
27545             // let's see if we can change the selection...
27546             sel = ev.target;
27547          
27548               var nodeRange = sel.ownerDocument.createRange();
27549             try {
27550                 nodeRange.selectNode(sel);
27551             } catch (e) {
27552                 nodeRange.selectNodeContents(sel);
27553             }
27554             //nodeRange.collapse(true);
27555             var s = this.editorcore.win.getSelection();
27556             s.removeAllRanges();
27557             s.addRange(nodeRange);
27558         }  
27559         
27560       
27561         var updateFooter = sel ? false : true;
27562         
27563         
27564         var ans = this.editorcore.getAllAncestors();
27565         
27566         // pick
27567         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27568         
27569         if (!sel) { 
27570             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
27571             sel = sel ? sel : this.editorcore.doc.body;
27572             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
27573             
27574         }
27575         // pick a menu that exists..
27576         var tn = sel.tagName.toUpperCase();
27577         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27578         
27579         tn = sel.tagName.toUpperCase();
27580         
27581         var lastSel = this.tb.selectedNode
27582         
27583         this.tb.selectedNode = sel;
27584         
27585         // if current menu does not match..
27586         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27587                 
27588             this.tb.el.hide();
27589             ///console.log("show: " + tn);
27590             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27591             this.tb.el.show();
27592             // update name
27593             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27594             
27595             
27596             // update attributes
27597             if (this.tb.fields) {
27598                 this.tb.fields.each(function(e) {
27599                     if (e.stylename) {
27600                         e.setValue(sel.style[e.stylename]);
27601                         return;
27602                     } 
27603                    e.setValue(sel.getAttribute(e.attrname));
27604                 });
27605             }
27606             
27607             var hasStyles = false;
27608             for(var i in this.styles) {
27609                 hasStyles = true;
27610                 break;
27611             }
27612             
27613             // update styles
27614             if (hasStyles) { 
27615                 var st = this.tb.fields.item(0);
27616                 
27617                 st.store.removeAll();
27618                
27619                 
27620                 var cn = sel.className.split(/\s+/);
27621                 
27622                 var avs = [];
27623                 if (this.styles['*']) {
27624                     
27625                     Roo.each(this.styles['*'], function(v) {
27626                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27627                     });
27628                 }
27629                 if (this.styles[tn]) { 
27630                     Roo.each(this.styles[tn], function(v) {
27631                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27632                     });
27633                 }
27634                 
27635                 st.store.loadData(avs);
27636                 st.collapse();
27637                 st.setValue(cn);
27638             }
27639             // flag our selected Node.
27640             this.tb.selectedNode = sel;
27641            
27642            
27643             Roo.menu.MenuMgr.hideAll();
27644
27645         }
27646         
27647         if (!updateFooter) {
27648             //this.footDisp.dom.innerHTML = ''; 
27649             return;
27650         }
27651         // update the footer
27652         //
27653         var html = '';
27654         
27655         this.footerEls = ans.reverse();
27656         Roo.each(this.footerEls, function(a,i) {
27657             if (!a) { return; }
27658             html += html.length ? ' &gt; '  :  '';
27659             
27660             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27661             
27662         });
27663        
27664         // 
27665         var sz = this.footDisp.up('td').getSize();
27666         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27667         this.footDisp.dom.style.marginLeft = '5px';
27668         
27669         this.footDisp.dom.style.overflow = 'hidden';
27670         
27671         this.footDisp.dom.innerHTML = html;
27672             
27673         //this.editorsyncValue();
27674     },
27675      
27676     
27677    
27678        
27679     // private
27680     onDestroy : function(){
27681         if(this.rendered){
27682             
27683             this.tb.items.each(function(item){
27684                 if(item.menu){
27685                     item.menu.removeAll();
27686                     if(item.menu.el){
27687                         item.menu.el.destroy();
27688                     }
27689                 }
27690                 item.destroy();
27691             });
27692              
27693         }
27694     },
27695     onFirstFocus: function() {
27696         // need to do this for all the toolbars..
27697         this.tb.items.each(function(item){
27698            item.enable();
27699         });
27700     },
27701     buildToolbar: function(tlist, nm)
27702     {
27703         var editor = this.editor;
27704         var editorcore = this.editorcore;
27705          // create a new element.
27706         var wdiv = editor.wrap.createChild({
27707                 tag: 'div'
27708             }, editor.wrap.dom.firstChild.nextSibling, true);
27709         
27710        
27711         var tb = new Roo.Toolbar(wdiv);
27712         // add the name..
27713         
27714         tb.add(nm+ ":&nbsp;");
27715         
27716         var styles = [];
27717         for(var i in this.styles) {
27718             styles.push(i);
27719         }
27720         
27721         // styles...
27722         if (styles && styles.length) {
27723             
27724             // this needs a multi-select checkbox...
27725             tb.addField( new Roo.form.ComboBox({
27726                 store: new Roo.data.SimpleStore({
27727                     id : 'val',
27728                     fields: ['val', 'selected'],
27729                     data : [] 
27730                 }),
27731                 name : '-roo-edit-className',
27732                 attrname : 'className',
27733                 displayField: 'val',
27734                 typeAhead: false,
27735                 mode: 'local',
27736                 editable : false,
27737                 triggerAction: 'all',
27738                 emptyText:'Select Style',
27739                 selectOnFocus:true,
27740                 width: 130,
27741                 listeners : {
27742                     'select': function(c, r, i) {
27743                         // initial support only for on class per el..
27744                         tb.selectedNode.className =  r ? r.get('val') : '';
27745                         editorcore.syncValue();
27746                     }
27747                 }
27748     
27749             }));
27750         }
27751         
27752         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27753         var tbops = tbc.options;
27754         
27755         for (var i in tlist) {
27756             
27757             var item = tlist[i];
27758             tb.add(item.title + ":&nbsp;");
27759             
27760             
27761             //optname == used so you can configure the options available..
27762             var opts = item.opts ? item.opts : false;
27763             if (item.optname) {
27764                 opts = tbops[item.optname];
27765            
27766             }
27767             
27768             if (opts) {
27769                 // opts == pulldown..
27770                 tb.addField( new Roo.form.ComboBox({
27771                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27772                         id : 'val',
27773                         fields: ['val', 'display'],
27774                         data : opts  
27775                     }),
27776                     name : '-roo-edit-' + i,
27777                     attrname : i,
27778                     stylename : item.style ? item.style : false,
27779                     displayField: item.displayField ? item.displayField : 'val',
27780                     valueField :  'val',
27781                     typeAhead: false,
27782                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27783                     editable : false,
27784                     triggerAction: 'all',
27785                     emptyText:'Select',
27786                     selectOnFocus:true,
27787                     width: item.width ? item.width  : 130,
27788                     listeners : {
27789                         'select': function(c, r, i) {
27790                             if (c.stylename) {
27791                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27792                                 return;
27793                             }
27794                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27795                         }
27796                     }
27797
27798                 }));
27799                 continue;
27800                     
27801                  
27802                 
27803                 tb.addField( new Roo.form.TextField({
27804                     name: i,
27805                     width: 100,
27806                     //allowBlank:false,
27807                     value: ''
27808                 }));
27809                 continue;
27810             }
27811             tb.addField( new Roo.form.TextField({
27812                 name: '-roo-edit-' + i,
27813                 attrname : i,
27814                 
27815                 width: item.width,
27816                 //allowBlank:true,
27817                 value: '',
27818                 listeners: {
27819                     'change' : function(f, nv, ov) {
27820                         tb.selectedNode.setAttribute(f.attrname, nv);
27821                     }
27822                 }
27823             }));
27824              
27825         }
27826         tb.addFill();
27827         var _this = this;
27828         tb.addButton( {
27829             text: 'Remove Tag',
27830     
27831             listeners : {
27832                 click : function ()
27833                 {
27834                     // remove
27835                     // undo does not work.
27836                      
27837                     var sn = tb.selectedNode;
27838                     
27839                     var pn = sn.parentNode;
27840                     
27841                     var stn =  sn.childNodes[0];
27842                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27843                     while (sn.childNodes.length) {
27844                         var node = sn.childNodes[0];
27845                         sn.removeChild(node);
27846                         //Roo.log(node);
27847                         pn.insertBefore(node, sn);
27848                         
27849                     }
27850                     pn.removeChild(sn);
27851                     var range = editorcore.createRange();
27852         
27853                     range.setStart(stn,0);
27854                     range.setEnd(en,0); //????
27855                     //range.selectNode(sel);
27856                     
27857                     
27858                     var selection = editorcore.getSelection();
27859                     selection.removeAllRanges();
27860                     selection.addRange(range);
27861                     
27862                     
27863                     
27864                     //_this.updateToolbar(null, null, pn);
27865                     _this.updateToolbar(null, null, null);
27866                     _this.footDisp.dom.innerHTML = ''; 
27867                 }
27868             }
27869             
27870                     
27871                 
27872             
27873         });
27874         
27875         
27876         tb.el.on('click', function(e){
27877             e.preventDefault(); // what does this do?
27878         });
27879         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27880         tb.el.hide();
27881         tb.name = nm;
27882         // dont need to disable them... as they will get hidden
27883         return tb;
27884          
27885         
27886     },
27887     buildFooter : function()
27888     {
27889         
27890         var fel = this.editor.wrap.createChild();
27891         this.footer = new Roo.Toolbar(fel);
27892         // toolbar has scrolly on left / right?
27893         var footDisp= new Roo.Toolbar.Fill();
27894         var _t = this;
27895         this.footer.add(
27896             {
27897                 text : '&lt;',
27898                 xtype: 'Button',
27899                 handler : function() {
27900                     _t.footDisp.scrollTo('left',0,true)
27901                 }
27902             }
27903         );
27904         this.footer.add( footDisp );
27905         this.footer.add( 
27906             {
27907                 text : '&gt;',
27908                 xtype: 'Button',
27909                 handler : function() {
27910                     // no animation..
27911                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27912                 }
27913             }
27914         );
27915         var fel = Roo.get(footDisp.el);
27916         fel.addClass('x-editor-context');
27917         this.footDispWrap = fel; 
27918         this.footDispWrap.overflow  = 'hidden';
27919         
27920         this.footDisp = fel.createChild();
27921         this.footDispWrap.on('click', this.onContextClick, this)
27922         
27923         
27924     },
27925     onContextClick : function (ev,dom)
27926     {
27927         ev.preventDefault();
27928         var  cn = dom.className;
27929         //Roo.log(cn);
27930         if (!cn.match(/x-ed-loc-/)) {
27931             return;
27932         }
27933         var n = cn.split('-').pop();
27934         var ans = this.footerEls;
27935         var sel = ans[n];
27936         
27937          // pick
27938         var range = this.editorcore.createRange();
27939         
27940         range.selectNodeContents(sel);
27941         //range.selectNode(sel);
27942         
27943         
27944         var selection = this.editorcore.getSelection();
27945         selection.removeAllRanges();
27946         selection.addRange(range);
27947         
27948         
27949         
27950         this.updateToolbar(null, null, sel);
27951         
27952         
27953     }
27954     
27955     
27956     
27957     
27958     
27959 });
27960
27961
27962
27963
27964
27965 /*
27966  * Based on:
27967  * Ext JS Library 1.1.1
27968  * Copyright(c) 2006-2007, Ext JS, LLC.
27969  *
27970  * Originally Released Under LGPL - original licence link has changed is not relivant.
27971  *
27972  * Fork - LGPL
27973  * <script type="text/javascript">
27974  */
27975  
27976 /**
27977  * @class Roo.form.BasicForm
27978  * @extends Roo.util.Observable
27979  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27980  * @constructor
27981  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27982  * @param {Object} config Configuration options
27983  */
27984 Roo.form.BasicForm = function(el, config){
27985     this.allItems = [];
27986     this.childForms = [];
27987     Roo.apply(this, config);
27988     /*
27989      * The Roo.form.Field items in this form.
27990      * @type MixedCollection
27991      */
27992      
27993      
27994     this.items = new Roo.util.MixedCollection(false, function(o){
27995         return o.id || (o.id = Roo.id());
27996     });
27997     this.addEvents({
27998         /**
27999          * @event beforeaction
28000          * Fires before any action is performed. Return false to cancel the action.
28001          * @param {Form} this
28002          * @param {Action} action The action to be performed
28003          */
28004         beforeaction: true,
28005         /**
28006          * @event actionfailed
28007          * Fires when an action fails.
28008          * @param {Form} this
28009          * @param {Action} action The action that failed
28010          */
28011         actionfailed : true,
28012         /**
28013          * @event actioncomplete
28014          * Fires when an action is completed.
28015          * @param {Form} this
28016          * @param {Action} action The action that completed
28017          */
28018         actioncomplete : true
28019     });
28020     if(el){
28021         this.initEl(el);
28022     }
28023     Roo.form.BasicForm.superclass.constructor.call(this);
28024 };
28025
28026 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28027     /**
28028      * @cfg {String} method
28029      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28030      */
28031     /**
28032      * @cfg {DataReader} reader
28033      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28034      * This is optional as there is built-in support for processing JSON.
28035      */
28036     /**
28037      * @cfg {DataReader} errorReader
28038      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28039      * This is completely optional as there is built-in support for processing JSON.
28040      */
28041     /**
28042      * @cfg {String} url
28043      * The URL to use for form actions if one isn't supplied in the action options.
28044      */
28045     /**
28046      * @cfg {Boolean} fileUpload
28047      * Set to true if this form is a file upload.
28048      */
28049      
28050     /**
28051      * @cfg {Object} baseParams
28052      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28053      */
28054      /**
28055      
28056     /**
28057      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28058      */
28059     timeout: 30,
28060
28061     // private
28062     activeAction : null,
28063
28064     /**
28065      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28066      * or setValues() data instead of when the form was first created.
28067      */
28068     trackResetOnLoad : false,
28069     
28070     
28071     /**
28072      * childForms - used for multi-tab forms
28073      * @type {Array}
28074      */
28075     childForms : false,
28076     
28077     /**
28078      * allItems - full list of fields.
28079      * @type {Array}
28080      */
28081     allItems : false,
28082     
28083     /**
28084      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28085      * element by passing it or its id or mask the form itself by passing in true.
28086      * @type Mixed
28087      */
28088     waitMsgTarget : false,
28089
28090     // private
28091     initEl : function(el){
28092         this.el = Roo.get(el);
28093         this.id = this.el.id || Roo.id();
28094         this.el.on('submit', this.onSubmit, this);
28095         this.el.addClass('x-form');
28096     },
28097
28098     // private
28099     onSubmit : function(e){
28100         e.stopEvent();
28101     },
28102
28103     /**
28104      * Returns true if client-side validation on the form is successful.
28105      * @return Boolean
28106      */
28107     isValid : function(){
28108         var valid = true;
28109         this.items.each(function(f){
28110            if(!f.validate()){
28111                valid = false;
28112            }
28113         });
28114         return valid;
28115     },
28116
28117     /**
28118      * Returns true if any fields in this form have changed since their original load.
28119      * @return Boolean
28120      */
28121     isDirty : function(){
28122         var dirty = false;
28123         this.items.each(function(f){
28124            if(f.isDirty()){
28125                dirty = true;
28126                return false;
28127            }
28128         });
28129         return dirty;
28130     },
28131
28132     /**
28133      * Performs a predefined action (submit or load) or custom actions you define on this form.
28134      * @param {String} actionName The name of the action type
28135      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28136      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28137      * accept other config options):
28138      * <pre>
28139 Property          Type             Description
28140 ----------------  ---------------  ----------------------------------------------------------------------------------
28141 url               String           The url for the action (defaults to the form's url)
28142 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28143 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28144 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28145                                    validate the form on the client (defaults to false)
28146      * </pre>
28147      * @return {BasicForm} this
28148      */
28149     doAction : function(action, options){
28150         if(typeof action == 'string'){
28151             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28152         }
28153         if(this.fireEvent('beforeaction', this, action) !== false){
28154             this.beforeAction(action);
28155             action.run.defer(100, action);
28156         }
28157         return this;
28158     },
28159
28160     /**
28161      * Shortcut to do a submit action.
28162      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28163      * @return {BasicForm} this
28164      */
28165     submit : function(options){
28166         this.doAction('submit', options);
28167         return this;
28168     },
28169
28170     /**
28171      * Shortcut to do a load action.
28172      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28173      * @return {BasicForm} this
28174      */
28175     load : function(options){
28176         this.doAction('load', options);
28177         return this;
28178     },
28179
28180     /**
28181      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28182      * @param {Record} record The record to edit
28183      * @return {BasicForm} this
28184      */
28185     updateRecord : function(record){
28186         record.beginEdit();
28187         var fs = record.fields;
28188         fs.each(function(f){
28189             var field = this.findField(f.name);
28190             if(field){
28191                 record.set(f.name, field.getValue());
28192             }
28193         }, this);
28194         record.endEdit();
28195         return this;
28196     },
28197
28198     /**
28199      * Loads an Roo.data.Record into this form.
28200      * @param {Record} record The record to load
28201      * @return {BasicForm} this
28202      */
28203     loadRecord : function(record){
28204         this.setValues(record.data);
28205         return this;
28206     },
28207
28208     // private
28209     beforeAction : function(action){
28210         var o = action.options;
28211         
28212        
28213         if(this.waitMsgTarget === true){
28214             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28215         }else if(this.waitMsgTarget){
28216             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28217             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28218         }else {
28219             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28220         }
28221          
28222     },
28223
28224     // private
28225     afterAction : function(action, success){
28226         this.activeAction = null;
28227         var o = action.options;
28228         
28229         if(this.waitMsgTarget === true){
28230             this.el.unmask();
28231         }else if(this.waitMsgTarget){
28232             this.waitMsgTarget.unmask();
28233         }else{
28234             Roo.MessageBox.updateProgress(1);
28235             Roo.MessageBox.hide();
28236         }
28237          
28238         if(success){
28239             if(o.reset){
28240                 this.reset();
28241             }
28242             Roo.callback(o.success, o.scope, [this, action]);
28243             this.fireEvent('actioncomplete', this, action);
28244             
28245         }else{
28246             
28247             // failure condition..
28248             // we have a scenario where updates need confirming.
28249             // eg. if a locking scenario exists..
28250             // we look for { errors : { needs_confirm : true }} in the response.
28251             if (
28252                 (typeof(action.result) != 'undefined')  &&
28253                 (typeof(action.result.errors) != 'undefined')  &&
28254                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28255            ){
28256                 var _t = this;
28257                 Roo.MessageBox.confirm(
28258                     "Change requires confirmation",
28259                     action.result.errorMsg,
28260                     function(r) {
28261                         if (r != 'yes') {
28262                             return;
28263                         }
28264                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28265                     }
28266                     
28267                 );
28268                 
28269                 
28270                 
28271                 return;
28272             }
28273             
28274             Roo.callback(o.failure, o.scope, [this, action]);
28275             // show an error message if no failed handler is set..
28276             if (!this.hasListener('actionfailed')) {
28277                 Roo.MessageBox.alert("Error",
28278                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28279                         action.result.errorMsg :
28280                         "Saving Failed, please check your entries or try again"
28281                 );
28282             }
28283             
28284             this.fireEvent('actionfailed', this, action);
28285         }
28286         
28287     },
28288
28289     /**
28290      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28291      * @param {String} id The value to search for
28292      * @return Field
28293      */
28294     findField : function(id){
28295         var field = this.items.get(id);
28296         if(!field){
28297             this.items.each(function(f){
28298                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28299                     field = f;
28300                     return false;
28301                 }
28302             });
28303         }
28304         return field || null;
28305     },
28306
28307     /**
28308      * Add a secondary form to this one, 
28309      * Used to provide tabbed forms. One form is primary, with hidden values 
28310      * which mirror the elements from the other forms.
28311      * 
28312      * @param {Roo.form.Form} form to add.
28313      * 
28314      */
28315     addForm : function(form)
28316     {
28317        
28318         if (this.childForms.indexOf(form) > -1) {
28319             // already added..
28320             return;
28321         }
28322         this.childForms.push(form);
28323         var n = '';
28324         Roo.each(form.allItems, function (fe) {
28325             
28326             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28327             if (this.findField(n)) { // already added..
28328                 return;
28329             }
28330             var add = new Roo.form.Hidden({
28331                 name : n
28332             });
28333             add.render(this.el);
28334             
28335             this.add( add );
28336         }, this);
28337         
28338     },
28339     /**
28340      * Mark fields in this form invalid in bulk.
28341      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28342      * @return {BasicForm} this
28343      */
28344     markInvalid : function(errors){
28345         if(errors instanceof Array){
28346             for(var i = 0, len = errors.length; i < len; i++){
28347                 var fieldError = errors[i];
28348                 var f = this.findField(fieldError.id);
28349                 if(f){
28350                     f.markInvalid(fieldError.msg);
28351                 }
28352             }
28353         }else{
28354             var field, id;
28355             for(id in errors){
28356                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28357                     field.markInvalid(errors[id]);
28358                 }
28359             }
28360         }
28361         Roo.each(this.childForms || [], function (f) {
28362             f.markInvalid(errors);
28363         });
28364         
28365         return this;
28366     },
28367
28368     /**
28369      * Set values for fields in this form in bulk.
28370      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28371      * @return {BasicForm} this
28372      */
28373     setValues : function(values){
28374         if(values instanceof Array){ // array of objects
28375             for(var i = 0, len = values.length; i < len; i++){
28376                 var v = values[i];
28377                 var f = this.findField(v.id);
28378                 if(f){
28379                     f.setValue(v.value);
28380                     if(this.trackResetOnLoad){
28381                         f.originalValue = f.getValue();
28382                     }
28383                 }
28384             }
28385         }else{ // object hash
28386             var field, id;
28387             for(id in values){
28388                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28389                     
28390                     if (field.setFromData && 
28391                         field.valueField && 
28392                         field.displayField &&
28393                         // combos' with local stores can 
28394                         // be queried via setValue()
28395                         // to set their value..
28396                         (field.store && !field.store.isLocal)
28397                         ) {
28398                         // it's a combo
28399                         var sd = { };
28400                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28401                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28402                         field.setFromData(sd);
28403                         
28404                     } else {
28405                         field.setValue(values[id]);
28406                     }
28407                     
28408                     
28409                     if(this.trackResetOnLoad){
28410                         field.originalValue = field.getValue();
28411                     }
28412                 }
28413             }
28414         }
28415          
28416         Roo.each(this.childForms || [], function (f) {
28417             f.setValues(values);
28418         });
28419                 
28420         return this;
28421     },
28422
28423     /**
28424      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28425      * they are returned as an array.
28426      * @param {Boolean} asString
28427      * @return {Object}
28428      */
28429     getValues : function(asString){
28430         if (this.childForms) {
28431             // copy values from the child forms
28432             Roo.each(this.childForms, function (f) {
28433                 this.setValues(f.getValues());
28434             }, this);
28435         }
28436         
28437         
28438         
28439         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28440         if(asString === true){
28441             return fs;
28442         }
28443         return Roo.urlDecode(fs);
28444     },
28445     
28446     /**
28447      * Returns the fields in this form as an object with key/value pairs. 
28448      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28449      * @return {Object}
28450      */
28451     getFieldValues : function(with_hidden)
28452     {
28453         if (this.childForms) {
28454             // copy values from the child forms
28455             // should this call getFieldValues - probably not as we do not currently copy
28456             // hidden fields when we generate..
28457             Roo.each(this.childForms, function (f) {
28458                 this.setValues(f.getValues());
28459             }, this);
28460         }
28461         
28462         var ret = {};
28463         this.items.each(function(f){
28464             if (!f.getName()) {
28465                 return;
28466             }
28467             var v = f.getValue();
28468             if (f.inputType =='radio') {
28469                 if (typeof(ret[f.getName()]) == 'undefined') {
28470                     ret[f.getName()] = ''; // empty..
28471                 }
28472                 
28473                 if (!f.el.dom.checked) {
28474                     return;
28475                     
28476                 }
28477                 v = f.el.dom.value;
28478                 
28479             }
28480             
28481             // not sure if this supported any more..
28482             if ((typeof(v) == 'object') && f.getRawValue) {
28483                 v = f.getRawValue() ; // dates..
28484             }
28485             // combo boxes where name != hiddenName...
28486             if (f.name != f.getName()) {
28487                 ret[f.name] = f.getRawValue();
28488             }
28489             ret[f.getName()] = v;
28490         });
28491         
28492         return ret;
28493     },
28494
28495     /**
28496      * Clears all invalid messages in this form.
28497      * @return {BasicForm} this
28498      */
28499     clearInvalid : function(){
28500         this.items.each(function(f){
28501            f.clearInvalid();
28502         });
28503         
28504         Roo.each(this.childForms || [], function (f) {
28505             f.clearInvalid();
28506         });
28507         
28508         
28509         return this;
28510     },
28511
28512     /**
28513      * Resets this form.
28514      * @return {BasicForm} this
28515      */
28516     reset : function(){
28517         this.items.each(function(f){
28518             f.reset();
28519         });
28520         
28521         Roo.each(this.childForms || [], function (f) {
28522             f.reset();
28523         });
28524        
28525         
28526         return this;
28527     },
28528
28529     /**
28530      * Add Roo.form components to this form.
28531      * @param {Field} field1
28532      * @param {Field} field2 (optional)
28533      * @param {Field} etc (optional)
28534      * @return {BasicForm} this
28535      */
28536     add : function(){
28537         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28538         return this;
28539     },
28540
28541
28542     /**
28543      * Removes a field from the items collection (does NOT remove its markup).
28544      * @param {Field} field
28545      * @return {BasicForm} this
28546      */
28547     remove : function(field){
28548         this.items.remove(field);
28549         return this;
28550     },
28551
28552     /**
28553      * Looks at the fields in this form, checks them for an id attribute,
28554      * and calls applyTo on the existing dom element with that id.
28555      * @return {BasicForm} this
28556      */
28557     render : function(){
28558         this.items.each(function(f){
28559             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28560                 f.applyTo(f.id);
28561             }
28562         });
28563         return this;
28564     },
28565
28566     /**
28567      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28568      * @param {Object} values
28569      * @return {BasicForm} this
28570      */
28571     applyToFields : function(o){
28572         this.items.each(function(f){
28573            Roo.apply(f, o);
28574         });
28575         return this;
28576     },
28577
28578     /**
28579      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28580      * @param {Object} values
28581      * @return {BasicForm} this
28582      */
28583     applyIfToFields : function(o){
28584         this.items.each(function(f){
28585            Roo.applyIf(f, o);
28586         });
28587         return this;
28588     }
28589 });
28590
28591 // back compat
28592 Roo.BasicForm = Roo.form.BasicForm;/*
28593  * Based on:
28594  * Ext JS Library 1.1.1
28595  * Copyright(c) 2006-2007, Ext JS, LLC.
28596  *
28597  * Originally Released Under LGPL - original licence link has changed is not relivant.
28598  *
28599  * Fork - LGPL
28600  * <script type="text/javascript">
28601  */
28602
28603 /**
28604  * @class Roo.form.Form
28605  * @extends Roo.form.BasicForm
28606  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28607  * @constructor
28608  * @param {Object} config Configuration options
28609  */
28610 Roo.form.Form = function(config){
28611     var xitems =  [];
28612     if (config.items) {
28613         xitems = config.items;
28614         delete config.items;
28615     }
28616    
28617     
28618     Roo.form.Form.superclass.constructor.call(this, null, config);
28619     this.url = this.url || this.action;
28620     if(!this.root){
28621         this.root = new Roo.form.Layout(Roo.applyIf({
28622             id: Roo.id()
28623         }, config));
28624     }
28625     this.active = this.root;
28626     /**
28627      * Array of all the buttons that have been added to this form via {@link addButton}
28628      * @type Array
28629      */
28630     this.buttons = [];
28631     this.allItems = [];
28632     this.addEvents({
28633         /**
28634          * @event clientvalidation
28635          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28636          * @param {Form} this
28637          * @param {Boolean} valid true if the form has passed client-side validation
28638          */
28639         clientvalidation: true,
28640         /**
28641          * @event rendered
28642          * Fires when the form is rendered
28643          * @param {Roo.form.Form} form
28644          */
28645         rendered : true
28646     });
28647     
28648     if (this.progressUrl) {
28649             // push a hidden field onto the list of fields..
28650             this.addxtype( {
28651                     xns: Roo.form, 
28652                     xtype : 'Hidden', 
28653                     name : 'UPLOAD_IDENTIFIER' 
28654             });
28655         }
28656         
28657     
28658     Roo.each(xitems, this.addxtype, this);
28659     
28660     
28661     
28662 };
28663
28664 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28665     /**
28666      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28667      */
28668     /**
28669      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28670      */
28671     /**
28672      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28673      */
28674     buttonAlign:'center',
28675
28676     /**
28677      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28678      */
28679     minButtonWidth:75,
28680
28681     /**
28682      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28683      * This property cascades to child containers if not set.
28684      */
28685     labelAlign:'left',
28686
28687     /**
28688      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28689      * fires a looping event with that state. This is required to bind buttons to the valid
28690      * state using the config value formBind:true on the button.
28691      */
28692     monitorValid : false,
28693
28694     /**
28695      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28696      */
28697     monitorPoll : 200,
28698     
28699     /**
28700      * @cfg {String} progressUrl - Url to return progress data 
28701      */
28702     
28703     progressUrl : false,
28704   
28705     /**
28706      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28707      * fields are added and the column is closed. If no fields are passed the column remains open
28708      * until end() is called.
28709      * @param {Object} config The config to pass to the column
28710      * @param {Field} field1 (optional)
28711      * @param {Field} field2 (optional)
28712      * @param {Field} etc (optional)
28713      * @return Column The column container object
28714      */
28715     column : function(c){
28716         var col = new Roo.form.Column(c);
28717         this.start(col);
28718         if(arguments.length > 1){ // duplicate code required because of Opera
28719             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28720             this.end();
28721         }
28722         return col;
28723     },
28724
28725     /**
28726      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28727      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28728      * until end() is called.
28729      * @param {Object} config The config to pass to the fieldset
28730      * @param {Field} field1 (optional)
28731      * @param {Field} field2 (optional)
28732      * @param {Field} etc (optional)
28733      * @return FieldSet The fieldset container object
28734      */
28735     fieldset : function(c){
28736         var fs = new Roo.form.FieldSet(c);
28737         this.start(fs);
28738         if(arguments.length > 1){ // duplicate code required because of Opera
28739             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28740             this.end();
28741         }
28742         return fs;
28743     },
28744
28745     /**
28746      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28747      * fields are added and the container is closed. If no fields are passed the container remains open
28748      * until end() is called.
28749      * @param {Object} config The config to pass to the Layout
28750      * @param {Field} field1 (optional)
28751      * @param {Field} field2 (optional)
28752      * @param {Field} etc (optional)
28753      * @return Layout The container object
28754      */
28755     container : function(c){
28756         var l = new Roo.form.Layout(c);
28757         this.start(l);
28758         if(arguments.length > 1){ // duplicate code required because of Opera
28759             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28760             this.end();
28761         }
28762         return l;
28763     },
28764
28765     /**
28766      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28767      * @param {Object} container A Roo.form.Layout or subclass of Layout
28768      * @return {Form} this
28769      */
28770     start : function(c){
28771         // cascade label info
28772         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28773         this.active.stack.push(c);
28774         c.ownerCt = this.active;
28775         this.active = c;
28776         return this;
28777     },
28778
28779     /**
28780      * Closes the current open container
28781      * @return {Form} this
28782      */
28783     end : function(){
28784         if(this.active == this.root){
28785             return this;
28786         }
28787         this.active = this.active.ownerCt;
28788         return this;
28789     },
28790
28791     /**
28792      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28793      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28794      * as the label of the field.
28795      * @param {Field} field1
28796      * @param {Field} field2 (optional)
28797      * @param {Field} etc. (optional)
28798      * @return {Form} this
28799      */
28800     add : function(){
28801         this.active.stack.push.apply(this.active.stack, arguments);
28802         this.allItems.push.apply(this.allItems,arguments);
28803         var r = [];
28804         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28805             if(a[i].isFormField){
28806                 r.push(a[i]);
28807             }
28808         }
28809         if(r.length > 0){
28810             Roo.form.Form.superclass.add.apply(this, r);
28811         }
28812         return this;
28813     },
28814     
28815
28816     
28817     
28818     
28819      /**
28820      * Find any element that has been added to a form, using it's ID or name
28821      * This can include framesets, columns etc. along with regular fields..
28822      * @param {String} id - id or name to find.
28823      
28824      * @return {Element} e - or false if nothing found.
28825      */
28826     findbyId : function(id)
28827     {
28828         var ret = false;
28829         if (!id) {
28830             return ret;
28831         }
28832         Roo.each(this.allItems, function(f){
28833             if (f.id == id || f.name == id ){
28834                 ret = f;
28835                 return false;
28836             }
28837         });
28838         return ret;
28839     },
28840
28841     
28842     
28843     /**
28844      * Render this form into the passed container. This should only be called once!
28845      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28846      * @return {Form} this
28847      */
28848     render : function(ct)
28849     {
28850         
28851         
28852         
28853         ct = Roo.get(ct);
28854         var o = this.autoCreate || {
28855             tag: 'form',
28856             method : this.method || 'POST',
28857             id : this.id || Roo.id()
28858         };
28859         this.initEl(ct.createChild(o));
28860
28861         this.root.render(this.el);
28862         
28863        
28864              
28865         this.items.each(function(f){
28866             f.render('x-form-el-'+f.id);
28867         });
28868
28869         if(this.buttons.length > 0){
28870             // tables are required to maintain order and for correct IE layout
28871             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28872                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28873                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28874             }}, null, true);
28875             var tr = tb.getElementsByTagName('tr')[0];
28876             for(var i = 0, len = this.buttons.length; i < len; i++) {
28877                 var b = this.buttons[i];
28878                 var td = document.createElement('td');
28879                 td.className = 'x-form-btn-td';
28880                 b.render(tr.appendChild(td));
28881             }
28882         }
28883         if(this.monitorValid){ // initialize after render
28884             this.startMonitoring();
28885         }
28886         this.fireEvent('rendered', this);
28887         return this;
28888     },
28889
28890     /**
28891      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28892      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28893      * object or a valid Roo.DomHelper element config
28894      * @param {Function} handler The function called when the button is clicked
28895      * @param {Object} scope (optional) The scope of the handler function
28896      * @return {Roo.Button}
28897      */
28898     addButton : function(config, handler, scope){
28899         var bc = {
28900             handler: handler,
28901             scope: scope,
28902             minWidth: this.minButtonWidth,
28903             hideParent:true
28904         };
28905         if(typeof config == "string"){
28906             bc.text = config;
28907         }else{
28908             Roo.apply(bc, config);
28909         }
28910         var btn = new Roo.Button(null, bc);
28911         this.buttons.push(btn);
28912         return btn;
28913     },
28914
28915      /**
28916      * Adds a series of form elements (using the xtype property as the factory method.
28917      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28918      * @param {Object} config 
28919      */
28920     
28921     addxtype : function()
28922     {
28923         var ar = Array.prototype.slice.call(arguments, 0);
28924         var ret = false;
28925         for(var i = 0; i < ar.length; i++) {
28926             if (!ar[i]) {
28927                 continue; // skip -- if this happends something invalid got sent, we 
28928                 // should ignore it, as basically that interface element will not show up
28929                 // and that should be pretty obvious!!
28930             }
28931             
28932             if (Roo.form[ar[i].xtype]) {
28933                 ar[i].form = this;
28934                 var fe = Roo.factory(ar[i], Roo.form);
28935                 if (!ret) {
28936                     ret = fe;
28937                 }
28938                 fe.form = this;
28939                 if (fe.store) {
28940                     fe.store.form = this;
28941                 }
28942                 if (fe.isLayout) {  
28943                          
28944                     this.start(fe);
28945                     this.allItems.push(fe);
28946                     if (fe.items && fe.addxtype) {
28947                         fe.addxtype.apply(fe, fe.items);
28948                         delete fe.items;
28949                     }
28950                      this.end();
28951                     continue;
28952                 }
28953                 
28954                 
28955                  
28956                 this.add(fe);
28957               //  console.log('adding ' + ar[i].xtype);
28958             }
28959             if (ar[i].xtype == 'Button') {  
28960                 //console.log('adding button');
28961                 //console.log(ar[i]);
28962                 this.addButton(ar[i]);
28963                 this.allItems.push(fe);
28964                 continue;
28965             }
28966             
28967             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28968                 alert('end is not supported on xtype any more, use items');
28969             //    this.end();
28970             //    //console.log('adding end');
28971             }
28972             
28973         }
28974         return ret;
28975     },
28976     
28977     /**
28978      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28979      * option "monitorValid"
28980      */
28981     startMonitoring : function(){
28982         if(!this.bound){
28983             this.bound = true;
28984             Roo.TaskMgr.start({
28985                 run : this.bindHandler,
28986                 interval : this.monitorPoll || 200,
28987                 scope: this
28988             });
28989         }
28990     },
28991
28992     /**
28993      * Stops monitoring of the valid state of this form
28994      */
28995     stopMonitoring : function(){
28996         this.bound = false;
28997     },
28998
28999     // private
29000     bindHandler : function(){
29001         if(!this.bound){
29002             return false; // stops binding
29003         }
29004         var valid = true;
29005         this.items.each(function(f){
29006             if(!f.isValid(true)){
29007                 valid = false;
29008                 return false;
29009             }
29010         });
29011         for(var i = 0, len = this.buttons.length; i < len; i++){
29012             var btn = this.buttons[i];
29013             if(btn.formBind === true && btn.disabled === valid){
29014                 btn.setDisabled(!valid);
29015             }
29016         }
29017         this.fireEvent('clientvalidation', this, valid);
29018     }
29019     
29020     
29021     
29022     
29023     
29024     
29025     
29026     
29027 });
29028
29029
29030 // back compat
29031 Roo.Form = Roo.form.Form;
29032 /*
29033  * Based on:
29034  * Ext JS Library 1.1.1
29035  * Copyright(c) 2006-2007, Ext JS, LLC.
29036  *
29037  * Originally Released Under LGPL - original licence link has changed is not relivant.
29038  *
29039  * Fork - LGPL
29040  * <script type="text/javascript">
29041  */
29042
29043 // as we use this in bootstrap.
29044 Roo.namespace('Roo.form');
29045  /**
29046  * @class Roo.form.Action
29047  * Internal Class used to handle form actions
29048  * @constructor
29049  * @param {Roo.form.BasicForm} el The form element or its id
29050  * @param {Object} config Configuration options
29051  */
29052
29053  
29054  
29055 // define the action interface
29056 Roo.form.Action = function(form, options){
29057     this.form = form;
29058     this.options = options || {};
29059 };
29060 /**
29061  * Client Validation Failed
29062  * @const 
29063  */
29064 Roo.form.Action.CLIENT_INVALID = 'client';
29065 /**
29066  * Server Validation Failed
29067  * @const 
29068  */
29069 Roo.form.Action.SERVER_INVALID = 'server';
29070  /**
29071  * Connect to Server Failed
29072  * @const 
29073  */
29074 Roo.form.Action.CONNECT_FAILURE = 'connect';
29075 /**
29076  * Reading Data from Server Failed
29077  * @const 
29078  */
29079 Roo.form.Action.LOAD_FAILURE = 'load';
29080
29081 Roo.form.Action.prototype = {
29082     type : 'default',
29083     failureType : undefined,
29084     response : undefined,
29085     result : undefined,
29086
29087     // interface method
29088     run : function(options){
29089
29090     },
29091
29092     // interface method
29093     success : function(response){
29094
29095     },
29096
29097     // interface method
29098     handleResponse : function(response){
29099
29100     },
29101
29102     // default connection failure
29103     failure : function(response){
29104         
29105         this.response = response;
29106         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29107         this.form.afterAction(this, false);
29108     },
29109
29110     processResponse : function(response){
29111         this.response = response;
29112         if(!response.responseText){
29113             return true;
29114         }
29115         this.result = this.handleResponse(response);
29116         return this.result;
29117     },
29118
29119     // utility functions used internally
29120     getUrl : function(appendParams){
29121         var url = this.options.url || this.form.url || this.form.el.dom.action;
29122         if(appendParams){
29123             var p = this.getParams();
29124             if(p){
29125                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29126             }
29127         }
29128         return url;
29129     },
29130
29131     getMethod : function(){
29132         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29133     },
29134
29135     getParams : function(){
29136         var bp = this.form.baseParams;
29137         var p = this.options.params;
29138         if(p){
29139             if(typeof p == "object"){
29140                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29141             }else if(typeof p == 'string' && bp){
29142                 p += '&' + Roo.urlEncode(bp);
29143             }
29144         }else if(bp){
29145             p = Roo.urlEncode(bp);
29146         }
29147         return p;
29148     },
29149
29150     createCallback : function(){
29151         return {
29152             success: this.success,
29153             failure: this.failure,
29154             scope: this,
29155             timeout: (this.form.timeout*1000),
29156             upload: this.form.fileUpload ? this.success : undefined
29157         };
29158     }
29159 };
29160
29161 Roo.form.Action.Submit = function(form, options){
29162     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29163 };
29164
29165 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29166     type : 'submit',
29167
29168     haveProgress : false,
29169     uploadComplete : false,
29170     
29171     // uploadProgress indicator.
29172     uploadProgress : function()
29173     {
29174         if (!this.form.progressUrl) {
29175             return;
29176         }
29177         
29178         if (!this.haveProgress) {
29179             Roo.MessageBox.progress("Uploading", "Uploading");
29180         }
29181         if (this.uploadComplete) {
29182            Roo.MessageBox.hide();
29183            return;
29184         }
29185         
29186         this.haveProgress = true;
29187    
29188         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29189         
29190         var c = new Roo.data.Connection();
29191         c.request({
29192             url : this.form.progressUrl,
29193             params: {
29194                 id : uid
29195             },
29196             method: 'GET',
29197             success : function(req){
29198                //console.log(data);
29199                 var rdata = false;
29200                 var edata;
29201                 try  {
29202                    rdata = Roo.decode(req.responseText)
29203                 } catch (e) {
29204                     Roo.log("Invalid data from server..");
29205                     Roo.log(edata);
29206                     return;
29207                 }
29208                 if (!rdata || !rdata.success) {
29209                     Roo.log(rdata);
29210                     Roo.MessageBox.alert(Roo.encode(rdata));
29211                     return;
29212                 }
29213                 var data = rdata.data;
29214                 
29215                 if (this.uploadComplete) {
29216                    Roo.MessageBox.hide();
29217                    return;
29218                 }
29219                    
29220                 if (data){
29221                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29222                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29223                     );
29224                 }
29225                 this.uploadProgress.defer(2000,this);
29226             },
29227        
29228             failure: function(data) {
29229                 Roo.log('progress url failed ');
29230                 Roo.log(data);
29231             },
29232             scope : this
29233         });
29234            
29235     },
29236     
29237     
29238     run : function()
29239     {
29240         // run get Values on the form, so it syncs any secondary forms.
29241         this.form.getValues();
29242         
29243         var o = this.options;
29244         var method = this.getMethod();
29245         var isPost = method == 'POST';
29246         if(o.clientValidation === false || this.form.isValid()){
29247             
29248             if (this.form.progressUrl) {
29249                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29250                     (new Date() * 1) + '' + Math.random());
29251                     
29252             } 
29253             
29254             
29255             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29256                 form:this.form.el.dom,
29257                 url:this.getUrl(!isPost),
29258                 method: method,
29259                 params:isPost ? this.getParams() : null,
29260                 isUpload: this.form.fileUpload
29261             }));
29262             
29263             this.uploadProgress();
29264
29265         }else if (o.clientValidation !== false){ // client validation failed
29266             this.failureType = Roo.form.Action.CLIENT_INVALID;
29267             this.form.afterAction(this, false);
29268         }
29269     },
29270
29271     success : function(response)
29272     {
29273         this.uploadComplete= true;
29274         if (this.haveProgress) {
29275             Roo.MessageBox.hide();
29276         }
29277         
29278         
29279         var result = this.processResponse(response);
29280         if(result === true || result.success){
29281             this.form.afterAction(this, true);
29282             return;
29283         }
29284         if(result.errors){
29285             this.form.markInvalid(result.errors);
29286             this.failureType = Roo.form.Action.SERVER_INVALID;
29287         }
29288         this.form.afterAction(this, false);
29289     },
29290     failure : function(response)
29291     {
29292         this.uploadComplete= true;
29293         if (this.haveProgress) {
29294             Roo.MessageBox.hide();
29295         }
29296         
29297         this.response = response;
29298         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29299         this.form.afterAction(this, false);
29300     },
29301     
29302     handleResponse : function(response){
29303         if(this.form.errorReader){
29304             var rs = this.form.errorReader.read(response);
29305             var errors = [];
29306             if(rs.records){
29307                 for(var i = 0, len = rs.records.length; i < len; i++) {
29308                     var r = rs.records[i];
29309                     errors[i] = r.data;
29310                 }
29311             }
29312             if(errors.length < 1){
29313                 errors = null;
29314             }
29315             return {
29316                 success : rs.success,
29317                 errors : errors
29318             };
29319         }
29320         var ret = false;
29321         try {
29322             ret = Roo.decode(response.responseText);
29323         } catch (e) {
29324             ret = {
29325                 success: false,
29326                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29327                 errors : []
29328             };
29329         }
29330         return ret;
29331         
29332     }
29333 });
29334
29335
29336 Roo.form.Action.Load = function(form, options){
29337     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29338     this.reader = this.form.reader;
29339 };
29340
29341 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29342     type : 'load',
29343
29344     run : function(){
29345         
29346         Roo.Ajax.request(Roo.apply(
29347                 this.createCallback(), {
29348                     method:this.getMethod(),
29349                     url:this.getUrl(false),
29350                     params:this.getParams()
29351         }));
29352     },
29353
29354     success : function(response){
29355         
29356         var result = this.processResponse(response);
29357         if(result === true || !result.success || !result.data){
29358             this.failureType = Roo.form.Action.LOAD_FAILURE;
29359             this.form.afterAction(this, false);
29360             return;
29361         }
29362         this.form.clearInvalid();
29363         this.form.setValues(result.data);
29364         this.form.afterAction(this, true);
29365     },
29366
29367     handleResponse : function(response){
29368         if(this.form.reader){
29369             var rs = this.form.reader.read(response);
29370             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29371             return {
29372                 success : rs.success,
29373                 data : data
29374             };
29375         }
29376         return Roo.decode(response.responseText);
29377     }
29378 });
29379
29380 Roo.form.Action.ACTION_TYPES = {
29381     'load' : Roo.form.Action.Load,
29382     'submit' : Roo.form.Action.Submit
29383 };/*
29384  * Based on:
29385  * Ext JS Library 1.1.1
29386  * Copyright(c) 2006-2007, Ext JS, LLC.
29387  *
29388  * Originally Released Under LGPL - original licence link has changed is not relivant.
29389  *
29390  * Fork - LGPL
29391  * <script type="text/javascript">
29392  */
29393  
29394 /**
29395  * @class Roo.form.Layout
29396  * @extends Roo.Component
29397  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29398  * @constructor
29399  * @param {Object} config Configuration options
29400  */
29401 Roo.form.Layout = function(config){
29402     var xitems = [];
29403     if (config.items) {
29404         xitems = config.items;
29405         delete config.items;
29406     }
29407     Roo.form.Layout.superclass.constructor.call(this, config);
29408     this.stack = [];
29409     Roo.each(xitems, this.addxtype, this);
29410      
29411 };
29412
29413 Roo.extend(Roo.form.Layout, Roo.Component, {
29414     /**
29415      * @cfg {String/Object} autoCreate
29416      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29417      */
29418     /**
29419      * @cfg {String/Object/Function} style
29420      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29421      * a function which returns such a specification.
29422      */
29423     /**
29424      * @cfg {String} labelAlign
29425      * Valid values are "left," "top" and "right" (defaults to "left")
29426      */
29427     /**
29428      * @cfg {Number} labelWidth
29429      * Fixed width in pixels of all field labels (defaults to undefined)
29430      */
29431     /**
29432      * @cfg {Boolean} clear
29433      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29434      */
29435     clear : true,
29436     /**
29437      * @cfg {String} labelSeparator
29438      * The separator to use after field labels (defaults to ':')
29439      */
29440     labelSeparator : ':',
29441     /**
29442      * @cfg {Boolean} hideLabels
29443      * True to suppress the display of field labels in this layout (defaults to false)
29444      */
29445     hideLabels : false,
29446
29447     // private
29448     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29449     
29450     isLayout : true,
29451     
29452     // private
29453     onRender : function(ct, position){
29454         if(this.el){ // from markup
29455             this.el = Roo.get(this.el);
29456         }else {  // generate
29457             var cfg = this.getAutoCreate();
29458             this.el = ct.createChild(cfg, position);
29459         }
29460         if(this.style){
29461             this.el.applyStyles(this.style);
29462         }
29463         if(this.labelAlign){
29464             this.el.addClass('x-form-label-'+this.labelAlign);
29465         }
29466         if(this.hideLabels){
29467             this.labelStyle = "display:none";
29468             this.elementStyle = "padding-left:0;";
29469         }else{
29470             if(typeof this.labelWidth == 'number'){
29471                 this.labelStyle = "width:"+this.labelWidth+"px;";
29472                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29473             }
29474             if(this.labelAlign == 'top'){
29475                 this.labelStyle = "width:auto;";
29476                 this.elementStyle = "padding-left:0;";
29477             }
29478         }
29479         var stack = this.stack;
29480         var slen = stack.length;
29481         if(slen > 0){
29482             if(!this.fieldTpl){
29483                 var t = new Roo.Template(
29484                     '<div class="x-form-item {5}">',
29485                         '<label for="{0}" style="{2}">{1}{4}</label>',
29486                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29487                         '</div>',
29488                     '</div><div class="x-form-clear-left"></div>'
29489                 );
29490                 t.disableFormats = true;
29491                 t.compile();
29492                 Roo.form.Layout.prototype.fieldTpl = t;
29493             }
29494             for(var i = 0; i < slen; i++) {
29495                 if(stack[i].isFormField){
29496                     this.renderField(stack[i]);
29497                 }else{
29498                     this.renderComponent(stack[i]);
29499                 }
29500             }
29501         }
29502         if(this.clear){
29503             this.el.createChild({cls:'x-form-clear'});
29504         }
29505     },
29506
29507     // private
29508     renderField : function(f){
29509         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29510                f.id, //0
29511                f.fieldLabel, //1
29512                f.labelStyle||this.labelStyle||'', //2
29513                this.elementStyle||'', //3
29514                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29515                f.itemCls||this.itemCls||''  //5
29516        ], true).getPrevSibling());
29517     },
29518
29519     // private
29520     renderComponent : function(c){
29521         c.render(c.isLayout ? this.el : this.el.createChild());    
29522     },
29523     /**
29524      * Adds a object form elements (using the xtype property as the factory method.)
29525      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29526      * @param {Object} config 
29527      */
29528     addxtype : function(o)
29529     {
29530         // create the lement.
29531         o.form = this.form;
29532         var fe = Roo.factory(o, Roo.form);
29533         this.form.allItems.push(fe);
29534         this.stack.push(fe);
29535         
29536         if (fe.isFormField) {
29537             this.form.items.add(fe);
29538         }
29539          
29540         return fe;
29541     }
29542 });
29543
29544 /**
29545  * @class Roo.form.Column
29546  * @extends Roo.form.Layout
29547  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29548  * @constructor
29549  * @param {Object} config Configuration options
29550  */
29551 Roo.form.Column = function(config){
29552     Roo.form.Column.superclass.constructor.call(this, config);
29553 };
29554
29555 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29556     /**
29557      * @cfg {Number/String} width
29558      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29559      */
29560     /**
29561      * @cfg {String/Object} autoCreate
29562      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29563      */
29564
29565     // private
29566     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29567
29568     // private
29569     onRender : function(ct, position){
29570         Roo.form.Column.superclass.onRender.call(this, ct, position);
29571         if(this.width){
29572             this.el.setWidth(this.width);
29573         }
29574     }
29575 });
29576
29577
29578 /**
29579  * @class Roo.form.Row
29580  * @extends Roo.form.Layout
29581  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29582  * @constructor
29583  * @param {Object} config Configuration options
29584  */
29585
29586  
29587 Roo.form.Row = function(config){
29588     Roo.form.Row.superclass.constructor.call(this, config);
29589 };
29590  
29591 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29592       /**
29593      * @cfg {Number/String} width
29594      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29595      */
29596     /**
29597      * @cfg {Number/String} height
29598      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29599      */
29600     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29601     
29602     padWidth : 20,
29603     // private
29604     onRender : function(ct, position){
29605         //console.log('row render');
29606         if(!this.rowTpl){
29607             var t = new Roo.Template(
29608                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29609                     '<label for="{0}" style="{2}">{1}{4}</label>',
29610                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29611                     '</div>',
29612                 '</div>'
29613             );
29614             t.disableFormats = true;
29615             t.compile();
29616             Roo.form.Layout.prototype.rowTpl = t;
29617         }
29618         this.fieldTpl = this.rowTpl;
29619         
29620         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29621         var labelWidth = 100;
29622         
29623         if ((this.labelAlign != 'top')) {
29624             if (typeof this.labelWidth == 'number') {
29625                 labelWidth = this.labelWidth
29626             }
29627             this.padWidth =  20 + labelWidth;
29628             
29629         }
29630         
29631         Roo.form.Column.superclass.onRender.call(this, ct, position);
29632         if(this.width){
29633             this.el.setWidth(this.width);
29634         }
29635         if(this.height){
29636             this.el.setHeight(this.height);
29637         }
29638     },
29639     
29640     // private
29641     renderField : function(f){
29642         f.fieldEl = this.fieldTpl.append(this.el, [
29643                f.id, f.fieldLabel,
29644                f.labelStyle||this.labelStyle||'',
29645                this.elementStyle||'',
29646                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29647                f.itemCls||this.itemCls||'',
29648                f.width ? f.width + this.padWidth : 160 + this.padWidth
29649        ],true);
29650     }
29651 });
29652  
29653
29654 /**
29655  * @class Roo.form.FieldSet
29656  * @extends Roo.form.Layout
29657  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29658  * @constructor
29659  * @param {Object} config Configuration options
29660  */
29661 Roo.form.FieldSet = function(config){
29662     Roo.form.FieldSet.superclass.constructor.call(this, config);
29663 };
29664
29665 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29666     /**
29667      * @cfg {String} legend
29668      * The text to display as the legend for the FieldSet (defaults to '')
29669      */
29670     /**
29671      * @cfg {String/Object} autoCreate
29672      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29673      */
29674
29675     // private
29676     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29677
29678     // private
29679     onRender : function(ct, position){
29680         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29681         if(this.legend){
29682             this.setLegend(this.legend);
29683         }
29684     },
29685
29686     // private
29687     setLegend : function(text){
29688         if(this.rendered){
29689             this.el.child('legend').update(text);
29690         }
29691     }
29692 });/*
29693  * Based on:
29694  * Ext JS Library 1.1.1
29695  * Copyright(c) 2006-2007, Ext JS, LLC.
29696  *
29697  * Originally Released Under LGPL - original licence link has changed is not relivant.
29698  *
29699  * Fork - LGPL
29700  * <script type="text/javascript">
29701  */
29702 /**
29703  * @class Roo.form.VTypes
29704  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29705  * @singleton
29706  */
29707 Roo.form.VTypes = function(){
29708     // closure these in so they are only created once.
29709     var alpha = /^[a-zA-Z_]+$/;
29710     var alphanum = /^[a-zA-Z0-9_]+$/;
29711     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29712     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29713
29714     // All these messages and functions are configurable
29715     return {
29716         /**
29717          * The function used to validate email addresses
29718          * @param {String} value The email address
29719          */
29720         'email' : function(v){
29721             return email.test(v);
29722         },
29723         /**
29724          * The error text to display when the email validation function returns false
29725          * @type String
29726          */
29727         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29728         /**
29729          * The keystroke filter mask to be applied on email input
29730          * @type RegExp
29731          */
29732         'emailMask' : /[a-z0-9_\.\-@]/i,
29733
29734         /**
29735          * The function used to validate URLs
29736          * @param {String} value The URL
29737          */
29738         'url' : function(v){
29739             return url.test(v);
29740         },
29741         /**
29742          * The error text to display when the url validation function returns false
29743          * @type String
29744          */
29745         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29746         
29747         /**
29748          * The function used to validate alpha values
29749          * @param {String} value The value
29750          */
29751         'alpha' : function(v){
29752             return alpha.test(v);
29753         },
29754         /**
29755          * The error text to display when the alpha validation function returns false
29756          * @type String
29757          */
29758         'alphaText' : 'This field should only contain letters and _',
29759         /**
29760          * The keystroke filter mask to be applied on alpha input
29761          * @type RegExp
29762          */
29763         'alphaMask' : /[a-z_]/i,
29764
29765         /**
29766          * The function used to validate alphanumeric values
29767          * @param {String} value The value
29768          */
29769         'alphanum' : function(v){
29770             return alphanum.test(v);
29771         },
29772         /**
29773          * The error text to display when the alphanumeric validation function returns false
29774          * @type String
29775          */
29776         'alphanumText' : 'This field should only contain letters, numbers and _',
29777         /**
29778          * The keystroke filter mask to be applied on alphanumeric input
29779          * @type RegExp
29780          */
29781         'alphanumMask' : /[a-z0-9_]/i
29782     };
29783 }();//<script type="text/javascript">
29784
29785 /**
29786  * @class Roo.form.FCKeditor
29787  * @extends Roo.form.TextArea
29788  * Wrapper around the FCKEditor http://www.fckeditor.net
29789  * @constructor
29790  * Creates a new FCKeditor
29791  * @param {Object} config Configuration options
29792  */
29793 Roo.form.FCKeditor = function(config){
29794     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29795     this.addEvents({
29796          /**
29797          * @event editorinit
29798          * Fired when the editor is initialized - you can add extra handlers here..
29799          * @param {FCKeditor} this
29800          * @param {Object} the FCK object.
29801          */
29802         editorinit : true
29803     });
29804     
29805     
29806 };
29807 Roo.form.FCKeditor.editors = { };
29808 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29809 {
29810     //defaultAutoCreate : {
29811     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29812     //},
29813     // private
29814     /**
29815      * @cfg {Object} fck options - see fck manual for details.
29816      */
29817     fckconfig : false,
29818     
29819     /**
29820      * @cfg {Object} fck toolbar set (Basic or Default)
29821      */
29822     toolbarSet : 'Basic',
29823     /**
29824      * @cfg {Object} fck BasePath
29825      */ 
29826     basePath : '/fckeditor/',
29827     
29828     
29829     frame : false,
29830     
29831     value : '',
29832     
29833    
29834     onRender : function(ct, position)
29835     {
29836         if(!this.el){
29837             this.defaultAutoCreate = {
29838                 tag: "textarea",
29839                 style:"width:300px;height:60px;",
29840                 autocomplete: "off"
29841             };
29842         }
29843         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29844         /*
29845         if(this.grow){
29846             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29847             if(this.preventScrollbars){
29848                 this.el.setStyle("overflow", "hidden");
29849             }
29850             this.el.setHeight(this.growMin);
29851         }
29852         */
29853         //console.log('onrender' + this.getId() );
29854         Roo.form.FCKeditor.editors[this.getId()] = this;
29855          
29856
29857         this.replaceTextarea() ;
29858         
29859     },
29860     
29861     getEditor : function() {
29862         return this.fckEditor;
29863     },
29864     /**
29865      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29866      * @param {Mixed} value The value to set
29867      */
29868     
29869     
29870     setValue : function(value)
29871     {
29872         //console.log('setValue: ' + value);
29873         
29874         if(typeof(value) == 'undefined') { // not sure why this is happending...
29875             return;
29876         }
29877         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29878         
29879         //if(!this.el || !this.getEditor()) {
29880         //    this.value = value;
29881             //this.setValue.defer(100,this,[value]);    
29882         //    return;
29883         //} 
29884         
29885         if(!this.getEditor()) {
29886             return;
29887         }
29888         
29889         this.getEditor().SetData(value);
29890         
29891         //
29892
29893     },
29894
29895     /**
29896      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29897      * @return {Mixed} value The field value
29898      */
29899     getValue : function()
29900     {
29901         
29902         if (this.frame && this.frame.dom.style.display == 'none') {
29903             return Roo.form.FCKeditor.superclass.getValue.call(this);
29904         }
29905         
29906         if(!this.el || !this.getEditor()) {
29907            
29908            // this.getValue.defer(100,this); 
29909             return this.value;
29910         }
29911        
29912         
29913         var value=this.getEditor().GetData();
29914         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29915         return Roo.form.FCKeditor.superclass.getValue.call(this);
29916         
29917
29918     },
29919
29920     /**
29921      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29922      * @return {Mixed} value The field value
29923      */
29924     getRawValue : function()
29925     {
29926         if (this.frame && this.frame.dom.style.display == 'none') {
29927             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29928         }
29929         
29930         if(!this.el || !this.getEditor()) {
29931             //this.getRawValue.defer(100,this); 
29932             return this.value;
29933             return;
29934         }
29935         
29936         
29937         
29938         var value=this.getEditor().GetData();
29939         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29940         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29941          
29942     },
29943     
29944     setSize : function(w,h) {
29945         
29946         
29947         
29948         //if (this.frame && this.frame.dom.style.display == 'none') {
29949         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29950         //    return;
29951         //}
29952         //if(!this.el || !this.getEditor()) {
29953         //    this.setSize.defer(100,this, [w,h]); 
29954         //    return;
29955         //}
29956         
29957         
29958         
29959         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29960         
29961         this.frame.dom.setAttribute('width', w);
29962         this.frame.dom.setAttribute('height', h);
29963         this.frame.setSize(w,h);
29964         
29965     },
29966     
29967     toggleSourceEdit : function(value) {
29968         
29969       
29970          
29971         this.el.dom.style.display = value ? '' : 'none';
29972         this.frame.dom.style.display = value ?  'none' : '';
29973         
29974     },
29975     
29976     
29977     focus: function(tag)
29978     {
29979         if (this.frame.dom.style.display == 'none') {
29980             return Roo.form.FCKeditor.superclass.focus.call(this);
29981         }
29982         if(!this.el || !this.getEditor()) {
29983             this.focus.defer(100,this, [tag]); 
29984             return;
29985         }
29986         
29987         
29988         
29989         
29990         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29991         this.getEditor().Focus();
29992         if (tgs.length) {
29993             if (!this.getEditor().Selection.GetSelection()) {
29994                 this.focus.defer(100,this, [tag]); 
29995                 return;
29996             }
29997             
29998             
29999             var r = this.getEditor().EditorDocument.createRange();
30000             r.setStart(tgs[0],0);
30001             r.setEnd(tgs[0],0);
30002             this.getEditor().Selection.GetSelection().removeAllRanges();
30003             this.getEditor().Selection.GetSelection().addRange(r);
30004             this.getEditor().Focus();
30005         }
30006         
30007     },
30008     
30009     
30010     
30011     replaceTextarea : function()
30012     {
30013         if ( document.getElementById( this.getId() + '___Frame' ) )
30014             return ;
30015         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30016         //{
30017             // We must check the elements firstly using the Id and then the name.
30018         var oTextarea = document.getElementById( this.getId() );
30019         
30020         var colElementsByName = document.getElementsByName( this.getId() ) ;
30021          
30022         oTextarea.style.display = 'none' ;
30023
30024         if ( oTextarea.tabIndex ) {            
30025             this.TabIndex = oTextarea.tabIndex ;
30026         }
30027         
30028         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30029         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30030         this.frame = Roo.get(this.getId() + '___Frame')
30031     },
30032     
30033     _getConfigHtml : function()
30034     {
30035         var sConfig = '' ;
30036
30037         for ( var o in this.fckconfig ) {
30038             sConfig += sConfig.length > 0  ? '&amp;' : '';
30039             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30040         }
30041
30042         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30043     },
30044     
30045     
30046     _getIFrameHtml : function()
30047     {
30048         var sFile = 'fckeditor.html' ;
30049         /* no idea what this is about..
30050         try
30051         {
30052             if ( (/fcksource=true/i).test( window.top.location.search ) )
30053                 sFile = 'fckeditor.original.html' ;
30054         }
30055         catch (e) { 
30056         */
30057
30058         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30059         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30060         
30061         
30062         var html = '<iframe id="' + this.getId() +
30063             '___Frame" src="' + sLink +
30064             '" width="' + this.width +
30065             '" height="' + this.height + '"' +
30066             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30067             ' frameborder="0" scrolling="no"></iframe>' ;
30068
30069         return html ;
30070     },
30071     
30072     _insertHtmlBefore : function( html, element )
30073     {
30074         if ( element.insertAdjacentHTML )       {
30075             // IE
30076             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30077         } else { // Gecko
30078             var oRange = document.createRange() ;
30079             oRange.setStartBefore( element ) ;
30080             var oFragment = oRange.createContextualFragment( html );
30081             element.parentNode.insertBefore( oFragment, element ) ;
30082         }
30083     }
30084     
30085     
30086   
30087     
30088     
30089     
30090     
30091
30092 });
30093
30094 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30095
30096 function FCKeditor_OnComplete(editorInstance){
30097     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30098     f.fckEditor = editorInstance;
30099     //console.log("loaded");
30100     f.fireEvent('editorinit', f, editorInstance);
30101
30102   
30103
30104  
30105
30106
30107
30108
30109
30110
30111
30112
30113
30114
30115
30116
30117
30118
30119
30120 //<script type="text/javascript">
30121 /**
30122  * @class Roo.form.GridField
30123  * @extends Roo.form.Field
30124  * Embed a grid (or editable grid into a form)
30125  * STATUS ALPHA
30126  * 
30127  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30128  * it needs 
30129  * xgrid.store = Roo.data.Store
30130  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30131  * xgrid.store.reader = Roo.data.JsonReader 
30132  * 
30133  * 
30134  * @constructor
30135  * Creates a new GridField
30136  * @param {Object} config Configuration options
30137  */
30138 Roo.form.GridField = function(config){
30139     Roo.form.GridField.superclass.constructor.call(this, config);
30140      
30141 };
30142
30143 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30144     /**
30145      * @cfg {Number} width  - used to restrict width of grid..
30146      */
30147     width : 100,
30148     /**
30149      * @cfg {Number} height - used to restrict height of grid..
30150      */
30151     height : 50,
30152      /**
30153      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30154          * 
30155          *}
30156      */
30157     xgrid : false, 
30158     /**
30159      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30160      * {tag: "input", type: "checkbox", autocomplete: "off"})
30161      */
30162    // defaultAutoCreate : { tag: 'div' },
30163     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30164     /**
30165      * @cfg {String} addTitle Text to include for adding a title.
30166      */
30167     addTitle : false,
30168     //
30169     onResize : function(){
30170         Roo.form.Field.superclass.onResize.apply(this, arguments);
30171     },
30172
30173     initEvents : function(){
30174         // Roo.form.Checkbox.superclass.initEvents.call(this);
30175         // has no events...
30176        
30177     },
30178
30179
30180     getResizeEl : function(){
30181         return this.wrap;
30182     },
30183
30184     getPositionEl : function(){
30185         return this.wrap;
30186     },
30187
30188     // private
30189     onRender : function(ct, position){
30190         
30191         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30192         var style = this.style;
30193         delete this.style;
30194         
30195         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30196         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30197         this.viewEl = this.wrap.createChild({ tag: 'div' });
30198         if (style) {
30199             this.viewEl.applyStyles(style);
30200         }
30201         if (this.width) {
30202             this.viewEl.setWidth(this.width);
30203         }
30204         if (this.height) {
30205             this.viewEl.setHeight(this.height);
30206         }
30207         //if(this.inputValue !== undefined){
30208         //this.setValue(this.value);
30209         
30210         
30211         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30212         
30213         
30214         this.grid.render();
30215         this.grid.getDataSource().on('remove', this.refreshValue, this);
30216         this.grid.getDataSource().on('update', this.refreshValue, this);
30217         this.grid.on('afteredit', this.refreshValue, this);
30218  
30219     },
30220      
30221     
30222     /**
30223      * Sets the value of the item. 
30224      * @param {String} either an object  or a string..
30225      */
30226     setValue : function(v){
30227         //this.value = v;
30228         v = v || []; // empty set..
30229         // this does not seem smart - it really only affects memoryproxy grids..
30230         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30231             var ds = this.grid.getDataSource();
30232             // assumes a json reader..
30233             var data = {}
30234             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30235             ds.loadData( data);
30236         }
30237         // clear selection so it does not get stale.
30238         if (this.grid.sm) { 
30239             this.grid.sm.clearSelections();
30240         }
30241         
30242         Roo.form.GridField.superclass.setValue.call(this, v);
30243         this.refreshValue();
30244         // should load data in the grid really....
30245     },
30246     
30247     // private
30248     refreshValue: function() {
30249          var val = [];
30250         this.grid.getDataSource().each(function(r) {
30251             val.push(r.data);
30252         });
30253         this.el.dom.value = Roo.encode(val);
30254     }
30255     
30256      
30257     
30258     
30259 });/*
30260  * Based on:
30261  * Ext JS Library 1.1.1
30262  * Copyright(c) 2006-2007, Ext JS, LLC.
30263  *
30264  * Originally Released Under LGPL - original licence link has changed is not relivant.
30265  *
30266  * Fork - LGPL
30267  * <script type="text/javascript">
30268  */
30269 /**
30270  * @class Roo.form.DisplayField
30271  * @extends Roo.form.Field
30272  * A generic Field to display non-editable data.
30273  * @constructor
30274  * Creates a new Display Field item.
30275  * @param {Object} config Configuration options
30276  */
30277 Roo.form.DisplayField = function(config){
30278     Roo.form.DisplayField.superclass.constructor.call(this, config);
30279     
30280 };
30281
30282 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30283     inputType:      'hidden',
30284     allowBlank:     true,
30285     readOnly:         true,
30286     
30287  
30288     /**
30289      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30290      */
30291     focusClass : undefined,
30292     /**
30293      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30294      */
30295     fieldClass: 'x-form-field',
30296     
30297      /**
30298      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30299      */
30300     valueRenderer: undefined,
30301     
30302     width: 100,
30303     /**
30304      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30305      * {tag: "input", type: "checkbox", autocomplete: "off"})
30306      */
30307      
30308  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30309
30310     onResize : function(){
30311         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30312         
30313     },
30314
30315     initEvents : function(){
30316         // Roo.form.Checkbox.superclass.initEvents.call(this);
30317         // has no events...
30318        
30319     },
30320
30321
30322     getResizeEl : function(){
30323         return this.wrap;
30324     },
30325
30326     getPositionEl : function(){
30327         return this.wrap;
30328     },
30329
30330     // private
30331     onRender : function(ct, position){
30332         
30333         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30334         //if(this.inputValue !== undefined){
30335         this.wrap = this.el.wrap();
30336         
30337         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30338         
30339         if (this.bodyStyle) {
30340             this.viewEl.applyStyles(this.bodyStyle);
30341         }
30342         //this.viewEl.setStyle('padding', '2px');
30343         
30344         this.setValue(this.value);
30345         
30346     },
30347 /*
30348     // private
30349     initValue : Roo.emptyFn,
30350
30351   */
30352
30353         // private
30354     onClick : function(){
30355         
30356     },
30357
30358     /**
30359      * Sets the checked state of the checkbox.
30360      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30361      */
30362     setValue : function(v){
30363         this.value = v;
30364         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30365         // this might be called before we have a dom element..
30366         if (!this.viewEl) {
30367             return;
30368         }
30369         this.viewEl.dom.innerHTML = html;
30370         Roo.form.DisplayField.superclass.setValue.call(this, v);
30371
30372     }
30373 });/*
30374  * 
30375  * Licence- LGPL
30376  * 
30377  */
30378
30379 /**
30380  * @class Roo.form.DayPicker
30381  * @extends Roo.form.Field
30382  * A Day picker show [M] [T] [W] ....
30383  * @constructor
30384  * Creates a new Day Picker
30385  * @param {Object} config Configuration options
30386  */
30387 Roo.form.DayPicker= function(config){
30388     Roo.form.DayPicker.superclass.constructor.call(this, config);
30389      
30390 };
30391
30392 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30393     /**
30394      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30395      */
30396     focusClass : undefined,
30397     /**
30398      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30399      */
30400     fieldClass: "x-form-field",
30401    
30402     /**
30403      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30404      * {tag: "input", type: "checkbox", autocomplete: "off"})
30405      */
30406     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30407     
30408    
30409     actionMode : 'viewEl', 
30410     //
30411     // private
30412  
30413     inputType : 'hidden',
30414     
30415      
30416     inputElement: false, // real input element?
30417     basedOn: false, // ????
30418     
30419     isFormField: true, // not sure where this is needed!!!!
30420
30421     onResize : function(){
30422         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30423         if(!this.boxLabel){
30424             this.el.alignTo(this.wrap, 'c-c');
30425         }
30426     },
30427
30428     initEvents : function(){
30429         Roo.form.Checkbox.superclass.initEvents.call(this);
30430         this.el.on("click", this.onClick,  this);
30431         this.el.on("change", this.onClick,  this);
30432     },
30433
30434
30435     getResizeEl : function(){
30436         return this.wrap;
30437     },
30438
30439     getPositionEl : function(){
30440         return this.wrap;
30441     },
30442
30443     
30444     // private
30445     onRender : function(ct, position){
30446         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30447        
30448         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30449         
30450         var r1 = '<table><tr>';
30451         var r2 = '<tr class="x-form-daypick-icons">';
30452         for (var i=0; i < 7; i++) {
30453             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30454             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30455         }
30456         
30457         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30458         viewEl.select('img').on('click', this.onClick, this);
30459         this.viewEl = viewEl;   
30460         
30461         
30462         // this will not work on Chrome!!!
30463         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30464         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30465         
30466         
30467           
30468
30469     },
30470
30471     // private
30472     initValue : Roo.emptyFn,
30473
30474     /**
30475      * Returns the checked state of the checkbox.
30476      * @return {Boolean} True if checked, else false
30477      */
30478     getValue : function(){
30479         return this.el.dom.value;
30480         
30481     },
30482
30483         // private
30484     onClick : function(e){ 
30485         //this.setChecked(!this.checked);
30486         Roo.get(e.target).toggleClass('x-menu-item-checked');
30487         this.refreshValue();
30488         //if(this.el.dom.checked != this.checked){
30489         //    this.setValue(this.el.dom.checked);
30490        // }
30491     },
30492     
30493     // private
30494     refreshValue : function()
30495     {
30496         var val = '';
30497         this.viewEl.select('img',true).each(function(e,i,n)  {
30498             val += e.is(".x-menu-item-checked") ? String(n) : '';
30499         });
30500         this.setValue(val, true);
30501     },
30502
30503     /**
30504      * Sets the checked state of the checkbox.
30505      * On is always based on a string comparison between inputValue and the param.
30506      * @param {Boolean/String} value - the value to set 
30507      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30508      */
30509     setValue : function(v,suppressEvent){
30510         if (!this.el.dom) {
30511             return;
30512         }
30513         var old = this.el.dom.value ;
30514         this.el.dom.value = v;
30515         if (suppressEvent) {
30516             return ;
30517         }
30518          
30519         // update display..
30520         this.viewEl.select('img',true).each(function(e,i,n)  {
30521             
30522             var on = e.is(".x-menu-item-checked");
30523             var newv = v.indexOf(String(n)) > -1;
30524             if (on != newv) {
30525                 e.toggleClass('x-menu-item-checked');
30526             }
30527             
30528         });
30529         
30530         
30531         this.fireEvent('change', this, v, old);
30532         
30533         
30534     },
30535    
30536     // handle setting of hidden value by some other method!!?!?
30537     setFromHidden: function()
30538     {
30539         if(!this.el){
30540             return;
30541         }
30542         //console.log("SET FROM HIDDEN");
30543         //alert('setFrom hidden');
30544         this.setValue(this.el.dom.value);
30545     },
30546     
30547     onDestroy : function()
30548     {
30549         if(this.viewEl){
30550             Roo.get(this.viewEl).remove();
30551         }
30552          
30553         Roo.form.DayPicker.superclass.onDestroy.call(this);
30554     }
30555
30556 });/*
30557  * RooJS Library 1.1.1
30558  * Copyright(c) 2008-2011  Alan Knowles
30559  *
30560  * License - LGPL
30561  */
30562  
30563
30564 /**
30565  * @class Roo.form.ComboCheck
30566  * @extends Roo.form.ComboBox
30567  * A combobox for multiple select items.
30568  *
30569  * FIXME - could do with a reset button..
30570  * 
30571  * @constructor
30572  * Create a new ComboCheck
30573  * @param {Object} config Configuration options
30574  */
30575 Roo.form.ComboCheck = function(config){
30576     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30577     // should verify some data...
30578     // like
30579     // hiddenName = required..
30580     // displayField = required
30581     // valudField == required
30582     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30583     var _t = this;
30584     Roo.each(req, function(e) {
30585         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30586             throw "Roo.form.ComboCheck : missing value for: " + e;
30587         }
30588     });
30589     
30590     
30591 };
30592
30593 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30594      
30595      
30596     editable : false,
30597      
30598     selectedClass: 'x-menu-item-checked', 
30599     
30600     // private
30601     onRender : function(ct, position){
30602         var _t = this;
30603         
30604         
30605         
30606         if(!this.tpl){
30607             var cls = 'x-combo-list';
30608
30609             
30610             this.tpl =  new Roo.Template({
30611                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30612                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30613                    '<span>{' + this.displayField + '}</span>' +
30614                     '</div>' 
30615                 
30616             });
30617         }
30618  
30619         
30620         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30621         this.view.singleSelect = false;
30622         this.view.multiSelect = true;
30623         this.view.toggleSelect = true;
30624         this.pageTb.add(new Roo.Toolbar.Fill(), {
30625             
30626             text: 'Done',
30627             handler: function()
30628             {
30629                 _t.collapse();
30630             }
30631         });
30632     },
30633     
30634     onViewOver : function(e, t){
30635         // do nothing...
30636         return;
30637         
30638     },
30639     
30640     onViewClick : function(doFocus,index){
30641         return;
30642         
30643     },
30644     select: function () {
30645         //Roo.log("SELECT CALLED");
30646     },
30647      
30648     selectByValue : function(xv, scrollIntoView){
30649         var ar = this.getValueArray();
30650         var sels = [];
30651         
30652         Roo.each(ar, function(v) {
30653             if(v === undefined || v === null){
30654                 return;
30655             }
30656             var r = this.findRecord(this.valueField, v);
30657             if(r){
30658                 sels.push(this.store.indexOf(r))
30659                 
30660             }
30661         },this);
30662         this.view.select(sels);
30663         return false;
30664     },
30665     
30666     
30667     
30668     onSelect : function(record, index){
30669        // Roo.log("onselect Called");
30670        // this is only called by the clear button now..
30671         this.view.clearSelections();
30672         this.setValue('[]');
30673         if (this.value != this.valueBefore) {
30674             this.fireEvent('change', this, this.value, this.valueBefore);
30675             this.valueBefore = this.value;
30676         }
30677     },
30678     getValueArray : function()
30679     {
30680         var ar = [] ;
30681         
30682         try {
30683             //Roo.log(this.value);
30684             if (typeof(this.value) == 'undefined') {
30685                 return [];
30686             }
30687             var ar = Roo.decode(this.value);
30688             return  ar instanceof Array ? ar : []; //?? valid?
30689             
30690         } catch(e) {
30691             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30692             return [];
30693         }
30694          
30695     },
30696     expand : function ()
30697     {
30698         
30699         Roo.form.ComboCheck.superclass.expand.call(this);
30700         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30701         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30702         
30703
30704     },
30705     
30706     collapse : function(){
30707         Roo.form.ComboCheck.superclass.collapse.call(this);
30708         var sl = this.view.getSelectedIndexes();
30709         var st = this.store;
30710         var nv = [];
30711         var tv = [];
30712         var r;
30713         Roo.each(sl, function(i) {
30714             r = st.getAt(i);
30715             nv.push(r.get(this.valueField));
30716         },this);
30717         this.setValue(Roo.encode(nv));
30718         if (this.value != this.valueBefore) {
30719
30720             this.fireEvent('change', this, this.value, this.valueBefore);
30721             this.valueBefore = this.value;
30722         }
30723         
30724     },
30725     
30726     setValue : function(v){
30727         // Roo.log(v);
30728         this.value = v;
30729         
30730         var vals = this.getValueArray();
30731         var tv = [];
30732         Roo.each(vals, function(k) {
30733             var r = this.findRecord(this.valueField, k);
30734             if(r){
30735                 tv.push(r.data[this.displayField]);
30736             }else if(this.valueNotFoundText !== undefined){
30737                 tv.push( this.valueNotFoundText );
30738             }
30739         },this);
30740        // Roo.log(tv);
30741         
30742         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30743         this.hiddenField.value = v;
30744         this.value = v;
30745     }
30746     
30747 });/*
30748  * Based on:
30749  * Ext JS Library 1.1.1
30750  * Copyright(c) 2006-2007, Ext JS, LLC.
30751  *
30752  * Originally Released Under LGPL - original licence link has changed is not relivant.
30753  *
30754  * Fork - LGPL
30755  * <script type="text/javascript">
30756  */
30757  
30758 /**
30759  * @class Roo.form.Signature
30760  * @extends Roo.form.Field
30761  * Signature field.  
30762  * @constructor
30763  * 
30764  * @param {Object} config Configuration options
30765  */
30766
30767 Roo.form.Signature = function(config){
30768     Roo.form.Signature.superclass.constructor.call(this, config);
30769     
30770     this.addEvents({// not in used??
30771          /**
30772          * @event confirm
30773          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30774              * @param {Roo.form.Signature} combo This combo box
30775              */
30776         'confirm' : true,
30777         /**
30778          * @event reset
30779          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30780              * @param {Roo.form.ComboBox} combo This combo box
30781              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30782              */
30783         'reset' : true
30784     });
30785 };
30786
30787 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30788     /**
30789      * @cfg {Object} labels Label to use when rendering a form.
30790      * defaults to 
30791      * labels : { 
30792      *      clear : "Clear",
30793      *      confirm : "Confirm"
30794      *  }
30795      */
30796     labels : { 
30797         clear : "Clear",
30798         confirm : "Confirm"
30799     },
30800     /**
30801      * @cfg {Number} width The signature panel width (defaults to 300)
30802      */
30803     width: 300,
30804     /**
30805      * @cfg {Number} height The signature panel height (defaults to 100)
30806      */
30807     height : 100,
30808     /**
30809      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30810      */
30811     allowBlank : false,
30812     
30813     //private
30814     // {Object} signPanel The signature SVG panel element (defaults to {})
30815     signPanel : {},
30816     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30817     isMouseDown : false,
30818     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30819     isConfirmed : false,
30820     // {String} signatureTmp SVG mapping string (defaults to empty string)
30821     signatureTmp : '',
30822     
30823     
30824     defaultAutoCreate : { // modified by initCompnoent..
30825         tag: "input",
30826         type:"hidden"
30827     },
30828
30829     // private
30830     onRender : function(ct, position){
30831         
30832         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30833         
30834         this.wrap = this.el.wrap({
30835             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30836         });
30837         
30838         this.createToolbar(this);
30839         this.signPanel = this.wrap.createChild({
30840                 tag: 'div',
30841                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30842             }, this.el
30843         );
30844             
30845         this.svgID = Roo.id();
30846         this.svgEl = this.signPanel.createChild({
30847               xmlns : 'http://www.w3.org/2000/svg',
30848               tag : 'svg',
30849               id : this.svgID + "-svg",
30850               width: this.width,
30851               height: this.height,
30852               viewBox: '0 0 '+this.width+' '+this.height,
30853               cn : [
30854                 {
30855                     tag: "rect",
30856                     id: this.svgID + "-svg-r",
30857                     width: this.width,
30858                     height: this.height,
30859                     fill: "#ffa"
30860                 },
30861                 {
30862                     tag: "line",
30863                     id: this.svgID + "-svg-l",
30864                     x1: "0", // start
30865                     y1: (this.height*0.8), // start set the line in 80% of height
30866                     x2: this.width, // end
30867                     y2: (this.height*0.8), // end set the line in 80% of height
30868                     'stroke': "#666",
30869                     'stroke-width': "1",
30870                     'stroke-dasharray': "3",
30871                     'shape-rendering': "crispEdges",
30872                     'pointer-events': "none"
30873                 },
30874                 {
30875                     tag: "path",
30876                     id: this.svgID + "-svg-p",
30877                     'stroke': "navy",
30878                     'stroke-width': "3",
30879                     'fill': "none",
30880                     'pointer-events': 'none'
30881                 }
30882               ]
30883         });
30884         this.createSVG();
30885         this.svgBox = this.svgEl.dom.getScreenCTM();
30886     },
30887     createSVG : function(){ 
30888         var svg = this.signPanel;
30889         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30890         var t = this;
30891
30892         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30893         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30894         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30895         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30896         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30897         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30898         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30899         
30900     },
30901     isTouchEvent : function(e){
30902         return e.type.match(/^touch/);
30903     },
30904     getCoords : function (e) {
30905         var pt    = this.svgEl.dom.createSVGPoint();
30906         pt.x = e.clientX; 
30907         pt.y = e.clientY;
30908         if (this.isTouchEvent(e)) {
30909             pt.x =  e.targetTouches[0].clientX 
30910             pt.y = e.targetTouches[0].clientY;
30911         }
30912         var a = this.svgEl.dom.getScreenCTM();
30913         var b = a.inverse();
30914         var mx = pt.matrixTransform(b);
30915         return mx.x + ',' + mx.y;
30916     },
30917     //mouse event headler 
30918     down : function (e) {
30919         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30920         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30921         
30922         this.isMouseDown = true;
30923         
30924         e.preventDefault();
30925     },
30926     move : function (e) {
30927         if (this.isMouseDown) {
30928             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30929             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30930         }
30931         
30932         e.preventDefault();
30933     },
30934     up : function (e) {
30935         this.isMouseDown = false;
30936         var sp = this.signatureTmp.split(' ');
30937         
30938         if(sp.length > 1){
30939             if(!sp[sp.length-2].match(/^L/)){
30940                 sp.pop();
30941                 sp.pop();
30942                 sp.push("");
30943                 this.signatureTmp = sp.join(" ");
30944             }
30945         }
30946         if(this.getValue() != this.signatureTmp){
30947             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30948             this.isConfirmed = false;
30949         }
30950         e.preventDefault();
30951     },
30952     
30953     /**
30954      * Protected method that will not generally be called directly. It
30955      * is called when the editor creates its toolbar. Override this method if you need to
30956      * add custom toolbar buttons.
30957      * @param {HtmlEditor} editor
30958      */
30959     createToolbar : function(editor){
30960          function btn(id, toggle, handler){
30961             var xid = fid + '-'+ id ;
30962             return {
30963                 id : xid,
30964                 cmd : id,
30965                 cls : 'x-btn-icon x-edit-'+id,
30966                 enableToggle:toggle !== false,
30967                 scope: editor, // was editor...
30968                 handler:handler||editor.relayBtnCmd,
30969                 clickEvent:'mousedown',
30970                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
30971                 tabIndex:-1
30972             };
30973         }
30974         
30975         
30976         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
30977         this.tb = tb;
30978         this.tb.add(
30979            {
30980                 cls : ' x-signature-btn x-signature-'+id,
30981                 scope: editor, // was editor...
30982                 handler: this.reset,
30983                 clickEvent:'mousedown',
30984                 text: this.labels.clear
30985             },
30986             {
30987                  xtype : 'Fill',
30988                  xns: Roo.Toolbar
30989             }, 
30990             {
30991                 cls : '  x-signature-btn x-signature-'+id,
30992                 scope: editor, // was editor...
30993                 handler: this.confirmHandler,
30994                 clickEvent:'mousedown',
30995                 text: this.labels.confirm
30996             }
30997         );
30998     
30999     },
31000     //public
31001     /**
31002      * when user is clicked confirm then show this image.....
31003      * 
31004      * @return {String} Image Data URI
31005      */
31006     getImageDataURI : function(){
31007         var svg = this.svgEl.dom.parentNode.innerHTML;
31008         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31009         return src; 
31010     },
31011     /**
31012      * 
31013      * @return {Boolean} this.isConfirmed
31014      */
31015     getConfirmed : function(){
31016         return this.isConfirmed;
31017     },
31018     /**
31019      * 
31020      * @return {Number} this.width
31021      */
31022     getWidth : function(){
31023         return this.width;
31024     },
31025     /**
31026      * 
31027      * @return {Number} this.height
31028      */
31029     getHeight : function(){
31030         return this.height;
31031     },
31032     // private
31033     getSignature : function(){
31034         return this.signatureTmp;
31035     },
31036     // private
31037     reset : function(){
31038         this.signatureTmp = '';
31039         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31040         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31041         this.isConfirmed = false;
31042         Roo.form.Signature.superclass.reset.call(this);
31043     },
31044     setSignature : function(s){
31045         this.signatureTmp = s;
31046         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31047         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31048         this.setValue(s);
31049         this.isConfirmed = false;
31050         Roo.form.Signature.superclass.reset.call(this);
31051     }, 
31052     test : function(){
31053 //        Roo.log(this.signPanel.dom.contentWindow.up())
31054     },
31055     //private
31056     setConfirmed : function(){
31057         
31058         
31059         
31060 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31061     },
31062     // private
31063     confirmHandler : function(){
31064         if(!this.getSignature()){
31065             return;
31066         }
31067         
31068         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31069         this.setValue(this.getSignature());
31070         this.isConfirmed = true;
31071         
31072         this.fireEvent('confirm', this);
31073     },
31074     // private
31075     // Subclasses should provide the validation implementation by overriding this
31076     validateValue : function(value){
31077         if(this.allowBlank){
31078             return true;
31079         }
31080         
31081         if(this.isConfirmed){
31082             return true;
31083         }
31084         return false;
31085     }
31086 });/*
31087  * Based on:
31088  * Ext JS Library 1.1.1
31089  * Copyright(c) 2006-2007, Ext JS, LLC.
31090  *
31091  * Originally Released Under LGPL - original licence link has changed is not relivant.
31092  *
31093  * Fork - LGPL
31094  * <script type="text/javascript">
31095  */
31096  
31097
31098 /**
31099  * @class Roo.form.ComboBox
31100  * @extends Roo.form.TriggerField
31101  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31102  * @constructor
31103  * Create a new ComboBox.
31104  * @param {Object} config Configuration options
31105  */
31106 Roo.form.Select = function(config){
31107     Roo.form.Select.superclass.constructor.call(this, config);
31108      
31109 };
31110
31111 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31112     /**
31113      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31114      */
31115     /**
31116      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31117      * rendering into an Roo.Editor, defaults to false)
31118      */
31119     /**
31120      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31121      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31122      */
31123     /**
31124      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31125      */
31126     /**
31127      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31128      * the dropdown list (defaults to undefined, with no header element)
31129      */
31130
31131      /**
31132      * @cfg {String/Roo.Template} tpl The template to use to render the output
31133      */
31134      
31135     // private
31136     defaultAutoCreate : {tag: "select"  },
31137     /**
31138      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31139      */
31140     listWidth: undefined,
31141     /**
31142      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31143      * mode = 'remote' or 'text' if mode = 'local')
31144      */
31145     displayField: undefined,
31146     /**
31147      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31148      * mode = 'remote' or 'value' if mode = 'local'). 
31149      * Note: use of a valueField requires the user make a selection
31150      * in order for a value to be mapped.
31151      */
31152     valueField: undefined,
31153     
31154     
31155     /**
31156      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31157      * field's data value (defaults to the underlying DOM element's name)
31158      */
31159     hiddenName: undefined,
31160     /**
31161      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31162      */
31163     listClass: '',
31164     /**
31165      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31166      */
31167     selectedClass: 'x-combo-selected',
31168     /**
31169      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31170      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31171      * which displays a downward arrow icon).
31172      */
31173     triggerClass : 'x-form-arrow-trigger',
31174     /**
31175      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31176      */
31177     shadow:'sides',
31178     /**
31179      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31180      * anchor positions (defaults to 'tl-bl')
31181      */
31182     listAlign: 'tl-bl?',
31183     /**
31184      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31185      */
31186     maxHeight: 300,
31187     /**
31188      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31189      * query specified by the allQuery config option (defaults to 'query')
31190      */
31191     triggerAction: 'query',
31192     /**
31193      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31194      * (defaults to 4, does not apply if editable = false)
31195      */
31196     minChars : 4,
31197     /**
31198      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31199      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31200      */
31201     typeAhead: false,
31202     /**
31203      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31204      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31205      */
31206     queryDelay: 500,
31207     /**
31208      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31209      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31210      */
31211     pageSize: 0,
31212     /**
31213      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31214      * when editable = true (defaults to false)
31215      */
31216     selectOnFocus:false,
31217     /**
31218      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31219      */
31220     queryParam: 'query',
31221     /**
31222      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31223      * when mode = 'remote' (defaults to 'Loading...')
31224      */
31225     loadingText: 'Loading...',
31226     /**
31227      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31228      */
31229     resizable: false,
31230     /**
31231      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31232      */
31233     handleHeight : 8,
31234     /**
31235      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31236      * traditional select (defaults to true)
31237      */
31238     editable: true,
31239     /**
31240      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31241      */
31242     allQuery: '',
31243     /**
31244      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31245      */
31246     mode: 'remote',
31247     /**
31248      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31249      * listWidth has a higher value)
31250      */
31251     minListWidth : 70,
31252     /**
31253      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31254      * allow the user to set arbitrary text into the field (defaults to false)
31255      */
31256     forceSelection:false,
31257     /**
31258      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31259      * if typeAhead = true (defaults to 250)
31260      */
31261     typeAheadDelay : 250,
31262     /**
31263      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31264      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31265      */
31266     valueNotFoundText : undefined,
31267     
31268     /**
31269      * @cfg {String} defaultValue The value displayed after loading the store.
31270      */
31271     defaultValue: '',
31272     
31273     /**
31274      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31275      */
31276     blockFocus : false,
31277     
31278     /**
31279      * @cfg {Boolean} disableClear Disable showing of clear button.
31280      */
31281     disableClear : false,
31282     /**
31283      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31284      */
31285     alwaysQuery : false,
31286     
31287     //private
31288     addicon : false,
31289     editicon: false,
31290     
31291     // element that contains real text value.. (when hidden is used..)
31292      
31293     // private
31294     onRender : function(ct, position){
31295         Roo.form.Field.prototype.onRender.call(this, ct, position);
31296         
31297         if(this.store){
31298             this.store.on('beforeload', this.onBeforeLoad, this);
31299             this.store.on('load', this.onLoad, this);
31300             this.store.on('loadexception', this.onLoadException, this);
31301             this.store.load({});
31302         }
31303         
31304         
31305         
31306     },
31307
31308     // private
31309     initEvents : function(){
31310         //Roo.form.ComboBox.superclass.initEvents.call(this);
31311  
31312     },
31313
31314     onDestroy : function(){
31315        
31316         if(this.store){
31317             this.store.un('beforeload', this.onBeforeLoad, this);
31318             this.store.un('load', this.onLoad, this);
31319             this.store.un('loadexception', this.onLoadException, this);
31320         }
31321         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31322     },
31323
31324     // private
31325     fireKey : function(e){
31326         if(e.isNavKeyPress() && !this.list.isVisible()){
31327             this.fireEvent("specialkey", this, e);
31328         }
31329     },
31330
31331     // private
31332     onResize: function(w, h){
31333         
31334         return; 
31335     
31336         
31337     },
31338
31339     /**
31340      * Allow or prevent the user from directly editing the field text.  If false is passed,
31341      * the user will only be able to select from the items defined in the dropdown list.  This method
31342      * is the runtime equivalent of setting the 'editable' config option at config time.
31343      * @param {Boolean} value True to allow the user to directly edit the field text
31344      */
31345     setEditable : function(value){
31346          
31347     },
31348
31349     // private
31350     onBeforeLoad : function(){
31351         
31352         Roo.log("Select before load");
31353         return;
31354     
31355         this.innerList.update(this.loadingText ?
31356                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31357         //this.restrictHeight();
31358         this.selectedIndex = -1;
31359     },
31360
31361     // private
31362     onLoad : function(){
31363
31364     
31365         var dom = this.el.dom;
31366         dom.innerHTML = '';
31367          var od = dom.ownerDocument;
31368          
31369         if (this.emptyText) {
31370             var op = od.createElement('option');
31371             op.setAttribute('value', '');
31372             op.innerHTML = String.format('{0}', this.emptyText);
31373             dom.appendChild(op);
31374         }
31375         if(this.store.getCount() > 0){
31376            
31377             var vf = this.valueField;
31378             var df = this.displayField;
31379             this.store.data.each(function(r) {
31380                 // which colmsn to use... testing - cdoe / title..
31381                 var op = od.createElement('option');
31382                 op.setAttribute('value', r.data[vf]);
31383                 op.innerHTML = String.format('{0}', r.data[df]);
31384                 dom.appendChild(op);
31385             });
31386             if (typeof(this.defaultValue != 'undefined')) {
31387                 this.setValue(this.defaultValue);
31388             }
31389             
31390              
31391         }else{
31392             //this.onEmptyResults();
31393         }
31394         //this.el.focus();
31395     },
31396     // private
31397     onLoadException : function()
31398     {
31399         dom.innerHTML = '';
31400             
31401         Roo.log("Select on load exception");
31402         return;
31403     
31404         this.collapse();
31405         Roo.log(this.store.reader.jsonData);
31406         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31407             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31408         }
31409         
31410         
31411     },
31412     // private
31413     onTypeAhead : function(){
31414          
31415     },
31416
31417     // private
31418     onSelect : function(record, index){
31419         Roo.log('on select?');
31420         return;
31421         if(this.fireEvent('beforeselect', this, record, index) !== false){
31422             this.setFromData(index > -1 ? record.data : false);
31423             this.collapse();
31424             this.fireEvent('select', this, record, index);
31425         }
31426     },
31427
31428     /**
31429      * Returns the currently selected field value or empty string if no value is set.
31430      * @return {String} value The selected value
31431      */
31432     getValue : function(){
31433         var dom = this.el.dom;
31434         this.value = dom.options[dom.selectedIndex].value;
31435         return this.value;
31436         
31437     },
31438
31439     /**
31440      * Clears any text/value currently set in the field
31441      */
31442     clearValue : function(){
31443         this.value = '';
31444         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31445         
31446     },
31447
31448     /**
31449      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31450      * will be displayed in the field.  If the value does not match the data value of an existing item,
31451      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31452      * Otherwise the field will be blank (although the value will still be set).
31453      * @param {String} value The value to match
31454      */
31455     setValue : function(v){
31456         var d = this.el.dom;
31457         for (var i =0; i < d.options.length;i++) {
31458             if (v == d.options[i].value) {
31459                 d.selectedIndex = i;
31460                 this.value = v;
31461                 return;
31462             }
31463         }
31464         this.clearValue();
31465     },
31466     /**
31467      * @property {Object} the last set data for the element
31468      */
31469     
31470     lastData : false,
31471     /**
31472      * Sets the value of the field based on a object which is related to the record format for the store.
31473      * @param {Object} value the value to set as. or false on reset?
31474      */
31475     setFromData : function(o){
31476         Roo.log('setfrom data?');
31477          
31478         
31479         
31480     },
31481     // private
31482     reset : function(){
31483         this.clearValue();
31484     },
31485     // private
31486     findRecord : function(prop, value){
31487         
31488         return false;
31489     
31490         var record;
31491         if(this.store.getCount() > 0){
31492             this.store.each(function(r){
31493                 if(r.data[prop] == value){
31494                     record = r;
31495                     return false;
31496                 }
31497                 return true;
31498             });
31499         }
31500         return record;
31501     },
31502     
31503     getName: function()
31504     {
31505         // returns hidden if it's set..
31506         if (!this.rendered) {return ''};
31507         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31508         
31509     },
31510      
31511
31512     
31513
31514     // private
31515     onEmptyResults : function(){
31516         Roo.log('empty results');
31517         //this.collapse();
31518     },
31519
31520     /**
31521      * Returns true if the dropdown list is expanded, else false.
31522      */
31523     isExpanded : function(){
31524         return false;
31525     },
31526
31527     /**
31528      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31529      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31530      * @param {String} value The data value of the item to select
31531      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31532      * selected item if it is not currently in view (defaults to true)
31533      * @return {Boolean} True if the value matched an item in the list, else false
31534      */
31535     selectByValue : function(v, scrollIntoView){
31536         Roo.log('select By Value');
31537         return false;
31538     
31539         if(v !== undefined && v !== null){
31540             var r = this.findRecord(this.valueField || this.displayField, v);
31541             if(r){
31542                 this.select(this.store.indexOf(r), scrollIntoView);
31543                 return true;
31544             }
31545         }
31546         return false;
31547     },
31548
31549     /**
31550      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31551      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31552      * @param {Number} index The zero-based index of the list item to select
31553      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31554      * selected item if it is not currently in view (defaults to true)
31555      */
31556     select : function(index, scrollIntoView){
31557         Roo.log('select ');
31558         return  ;
31559         
31560         this.selectedIndex = index;
31561         this.view.select(index);
31562         if(scrollIntoView !== false){
31563             var el = this.view.getNode(index);
31564             if(el){
31565                 this.innerList.scrollChildIntoView(el, false);
31566             }
31567         }
31568     },
31569
31570       
31571
31572     // private
31573     validateBlur : function(){
31574         
31575         return;
31576         
31577     },
31578
31579     // private
31580     initQuery : function(){
31581         this.doQuery(this.getRawValue());
31582     },
31583
31584     // private
31585     doForce : function(){
31586         if(this.el.dom.value.length > 0){
31587             this.el.dom.value =
31588                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31589              
31590         }
31591     },
31592
31593     /**
31594      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31595      * query allowing the query action to be canceled if needed.
31596      * @param {String} query The SQL query to execute
31597      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31598      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31599      * saved in the current store (defaults to false)
31600      */
31601     doQuery : function(q, forceAll){
31602         
31603         Roo.log('doQuery?');
31604         if(q === undefined || q === null){
31605             q = '';
31606         }
31607         var qe = {
31608             query: q,
31609             forceAll: forceAll,
31610             combo: this,
31611             cancel:false
31612         };
31613         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31614             return false;
31615         }
31616         q = qe.query;
31617         forceAll = qe.forceAll;
31618         if(forceAll === true || (q.length >= this.minChars)){
31619             if(this.lastQuery != q || this.alwaysQuery){
31620                 this.lastQuery = q;
31621                 if(this.mode == 'local'){
31622                     this.selectedIndex = -1;
31623                     if(forceAll){
31624                         this.store.clearFilter();
31625                     }else{
31626                         this.store.filter(this.displayField, q);
31627                     }
31628                     this.onLoad();
31629                 }else{
31630                     this.store.baseParams[this.queryParam] = q;
31631                     this.store.load({
31632                         params: this.getParams(q)
31633                     });
31634                     this.expand();
31635                 }
31636             }else{
31637                 this.selectedIndex = -1;
31638                 this.onLoad();   
31639             }
31640         }
31641     },
31642
31643     // private
31644     getParams : function(q){
31645         var p = {};
31646         //p[this.queryParam] = q;
31647         if(this.pageSize){
31648             p.start = 0;
31649             p.limit = this.pageSize;
31650         }
31651         return p;
31652     },
31653
31654     /**
31655      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31656      */
31657     collapse : function(){
31658         
31659     },
31660
31661     // private
31662     collapseIf : function(e){
31663         
31664     },
31665
31666     /**
31667      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31668      */
31669     expand : function(){
31670         
31671     } ,
31672
31673     // private
31674      
31675
31676     /** 
31677     * @cfg {Boolean} grow 
31678     * @hide 
31679     */
31680     /** 
31681     * @cfg {Number} growMin 
31682     * @hide 
31683     */
31684     /** 
31685     * @cfg {Number} growMax 
31686     * @hide 
31687     */
31688     /**
31689      * @hide
31690      * @method autoSize
31691      */
31692     
31693     setWidth : function()
31694     {
31695         
31696     },
31697     getResizeEl : function(){
31698         return this.el;
31699     }
31700 });//<script type="text/javasscript">
31701  
31702
31703 /**
31704  * @class Roo.DDView
31705  * A DnD enabled version of Roo.View.
31706  * @param {Element/String} container The Element in which to create the View.
31707  * @param {String} tpl The template string used to create the markup for each element of the View
31708  * @param {Object} config The configuration properties. These include all the config options of
31709  * {@link Roo.View} plus some specific to this class.<br>
31710  * <p>
31711  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31712  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31713  * <p>
31714  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31715 .x-view-drag-insert-above {
31716         border-top:1px dotted #3366cc;
31717 }
31718 .x-view-drag-insert-below {
31719         border-bottom:1px dotted #3366cc;
31720 }
31721 </code></pre>
31722  * 
31723  */
31724  
31725 Roo.DDView = function(container, tpl, config) {
31726     Roo.DDView.superclass.constructor.apply(this, arguments);
31727     this.getEl().setStyle("outline", "0px none");
31728     this.getEl().unselectable();
31729     if (this.dragGroup) {
31730                 this.setDraggable(this.dragGroup.split(","));
31731     }
31732     if (this.dropGroup) {
31733                 this.setDroppable(this.dropGroup.split(","));
31734     }
31735     if (this.deletable) {
31736         this.setDeletable();
31737     }
31738     this.isDirtyFlag = false;
31739         this.addEvents({
31740                 "drop" : true
31741         });
31742 };
31743
31744 Roo.extend(Roo.DDView, Roo.View, {
31745 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31746 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31747 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31748 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31749
31750         isFormField: true,
31751
31752         reset: Roo.emptyFn,
31753         
31754         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31755
31756         validate: function() {
31757                 return true;
31758         },
31759         
31760         destroy: function() {
31761                 this.purgeListeners();
31762                 this.getEl.removeAllListeners();
31763                 this.getEl().remove();
31764                 if (this.dragZone) {
31765                         if (this.dragZone.destroy) {
31766                                 this.dragZone.destroy();
31767                         }
31768                 }
31769                 if (this.dropZone) {
31770                         if (this.dropZone.destroy) {
31771                                 this.dropZone.destroy();
31772                         }
31773                 }
31774         },
31775
31776 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31777         getName: function() {
31778                 return this.name;
31779         },
31780
31781 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31782         setValue: function(v) {
31783                 if (!this.store) {
31784                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31785                 }
31786                 var data = {};
31787                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31788                 this.store.proxy = new Roo.data.MemoryProxy(data);
31789                 this.store.load();
31790         },
31791
31792 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31793         getValue: function() {
31794                 var result = '(';
31795                 this.store.each(function(rec) {
31796                         result += rec.id + ',';
31797                 });
31798                 return result.substr(0, result.length - 1) + ')';
31799         },
31800         
31801         getIds: function() {
31802                 var i = 0, result = new Array(this.store.getCount());
31803                 this.store.each(function(rec) {
31804                         result[i++] = rec.id;
31805                 });
31806                 return result;
31807         },
31808         
31809         isDirty: function() {
31810                 return this.isDirtyFlag;
31811         },
31812
31813 /**
31814  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31815  *      whole Element becomes the target, and this causes the drop gesture to append.
31816  */
31817     getTargetFromEvent : function(e) {
31818                 var target = e.getTarget();
31819                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31820                 target = target.parentNode;
31821                 }
31822                 if (!target) {
31823                         target = this.el.dom.lastChild || this.el.dom;
31824                 }
31825                 return target;
31826     },
31827
31828 /**
31829  *      Create the drag data which consists of an object which has the property "ddel" as
31830  *      the drag proxy element. 
31831  */
31832     getDragData : function(e) {
31833         var target = this.findItemFromChild(e.getTarget());
31834                 if(target) {
31835                         this.handleSelection(e);
31836                         var selNodes = this.getSelectedNodes();
31837             var dragData = {
31838                 source: this,
31839                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31840                 nodes: selNodes,
31841                 records: []
31842                         };
31843                         var selectedIndices = this.getSelectedIndexes();
31844                         for (var i = 0; i < selectedIndices.length; i++) {
31845                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31846                         }
31847                         if (selNodes.length == 1) {
31848                                 dragData.ddel = target.cloneNode(true); // the div element
31849                         } else {
31850                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31851                                 div.className = 'multi-proxy';
31852                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31853                                         div.appendChild(selNodes[i].cloneNode(true));
31854                                 }
31855                                 dragData.ddel = div;
31856                         }
31857             //console.log(dragData)
31858             //console.log(dragData.ddel.innerHTML)
31859                         return dragData;
31860                 }
31861         //console.log('nodragData')
31862                 return false;
31863     },
31864     
31865 /**     Specify to which ddGroup items in this DDView may be dragged. */
31866     setDraggable: function(ddGroup) {
31867         if (ddGroup instanceof Array) {
31868                 Roo.each(ddGroup, this.setDraggable, this);
31869                 return;
31870         }
31871         if (this.dragZone) {
31872                 this.dragZone.addToGroup(ddGroup);
31873         } else {
31874                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31875                                 containerScroll: true,
31876                                 ddGroup: ddGroup 
31877
31878                         });
31879 //                      Draggability implies selection. DragZone's mousedown selects the element.
31880                         if (!this.multiSelect) { this.singleSelect = true; }
31881
31882 //                      Wire the DragZone's handlers up to methods in *this*
31883                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31884                 }
31885     },
31886
31887 /**     Specify from which ddGroup this DDView accepts drops. */
31888     setDroppable: function(ddGroup) {
31889         if (ddGroup instanceof Array) {
31890                 Roo.each(ddGroup, this.setDroppable, this);
31891                 return;
31892         }
31893         if (this.dropZone) {
31894                 this.dropZone.addToGroup(ddGroup);
31895         } else {
31896                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31897                                 containerScroll: true,
31898                                 ddGroup: ddGroup
31899                         });
31900
31901 //                      Wire the DropZone's handlers up to methods in *this*
31902                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31903                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31904                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31905                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31906                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31907                 }
31908     },
31909
31910 /**     Decide whether to drop above or below a View node. */
31911     getDropPoint : function(e, n, dd){
31912         if (n == this.el.dom) { return "above"; }
31913                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31914                 var c = t + (b - t) / 2;
31915                 var y = Roo.lib.Event.getPageY(e);
31916                 if(y <= c) {
31917                         return "above";
31918                 }else{
31919                         return "below";
31920                 }
31921     },
31922
31923     onNodeEnter : function(n, dd, e, data){
31924                 return false;
31925     },
31926     
31927     onNodeOver : function(n, dd, e, data){
31928                 var pt = this.getDropPoint(e, n, dd);
31929                 // set the insert point style on the target node
31930                 var dragElClass = this.dropNotAllowed;
31931                 if (pt) {
31932                         var targetElClass;
31933                         if (pt == "above"){
31934                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31935                                 targetElClass = "x-view-drag-insert-above";
31936                         } else {
31937                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31938                                 targetElClass = "x-view-drag-insert-below";
31939                         }
31940                         if (this.lastInsertClass != targetElClass){
31941                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31942                                 this.lastInsertClass = targetElClass;
31943                         }
31944                 }
31945                 return dragElClass;
31946         },
31947
31948     onNodeOut : function(n, dd, e, data){
31949                 this.removeDropIndicators(n);
31950     },
31951
31952     onNodeDrop : function(n, dd, e, data){
31953         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31954                 return false;
31955         }
31956         var pt = this.getDropPoint(e, n, dd);
31957                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
31958                 if (pt == "below") { insertAt++; }
31959                 for (var i = 0; i < data.records.length; i++) {
31960                         var r = data.records[i];
31961                         var dup = this.store.getById(r.id);
31962                         if (dup && (dd != this.dragZone)) {
31963                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
31964                         } else {
31965                                 if (data.copy) {
31966                                         this.store.insert(insertAt++, r.copy());
31967                                 } else {
31968                                         data.source.isDirtyFlag = true;
31969                                         r.store.remove(r);
31970                                         this.store.insert(insertAt++, r);
31971                                 }
31972                                 this.isDirtyFlag = true;
31973                         }
31974                 }
31975                 this.dragZone.cachedTarget = null;
31976                 return true;
31977     },
31978
31979     removeDropIndicators : function(n){
31980                 if(n){
31981                         Roo.fly(n).removeClass([
31982                                 "x-view-drag-insert-above",
31983                                 "x-view-drag-insert-below"]);
31984                         this.lastInsertClass = "_noclass";
31985                 }
31986     },
31987
31988 /**
31989  *      Utility method. Add a delete option to the DDView's context menu.
31990  *      @param {String} imageUrl The URL of the "delete" icon image.
31991  */
31992         setDeletable: function(imageUrl) {
31993                 if (!this.singleSelect && !this.multiSelect) {
31994                         this.singleSelect = true;
31995                 }
31996                 var c = this.getContextMenu();
31997                 this.contextMenu.on("itemclick", function(item) {
31998                         switch (item.id) {
31999                                 case "delete":
32000                                         this.remove(this.getSelectedIndexes());
32001                                         break;
32002                         }
32003                 }, this);
32004                 this.contextMenu.add({
32005                         icon: imageUrl,
32006                         id: "delete",
32007                         text: 'Delete'
32008                 });
32009         },
32010         
32011 /**     Return the context menu for this DDView. */
32012         getContextMenu: function() {
32013                 if (!this.contextMenu) {
32014 //                      Create the View's context menu
32015                         this.contextMenu = new Roo.menu.Menu({
32016                                 id: this.id + "-contextmenu"
32017                         });
32018                         this.el.on("contextmenu", this.showContextMenu, this);
32019                 }
32020                 return this.contextMenu;
32021         },
32022         
32023         disableContextMenu: function() {
32024                 if (this.contextMenu) {
32025                         this.el.un("contextmenu", this.showContextMenu, this);
32026                 }
32027         },
32028
32029         showContextMenu: function(e, item) {
32030         item = this.findItemFromChild(e.getTarget());
32031                 if (item) {
32032                         e.stopEvent();
32033                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32034                         this.contextMenu.showAt(e.getXY());
32035             }
32036     },
32037
32038 /**
32039  *      Remove {@link Roo.data.Record}s at the specified indices.
32040  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32041  */
32042     remove: function(selectedIndices) {
32043                 selectedIndices = [].concat(selectedIndices);
32044                 for (var i = 0; i < selectedIndices.length; i++) {
32045                         var rec = this.store.getAt(selectedIndices[i]);
32046                         this.store.remove(rec);
32047                 }
32048     },
32049
32050 /**
32051  *      Double click fires the event, but also, if this is draggable, and there is only one other
32052  *      related DropZone, it transfers the selected node.
32053  */
32054     onDblClick : function(e){
32055         var item = this.findItemFromChild(e.getTarget());
32056         if(item){
32057             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32058                 return false;
32059             }
32060             if (this.dragGroup) {
32061                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32062                     while (targets.indexOf(this.dropZone) > -1) {
32063                             targets.remove(this.dropZone);
32064                                 }
32065                     if (targets.length == 1) {
32066                                         this.dragZone.cachedTarget = null;
32067                         var el = Roo.get(targets[0].getEl());
32068                         var box = el.getBox(true);
32069                         targets[0].onNodeDrop(el.dom, {
32070                                 target: el.dom,
32071                                 xy: [box.x, box.y + box.height - 1]
32072                         }, null, this.getDragData(e));
32073                     }
32074                 }
32075         }
32076     },
32077     
32078     handleSelection: function(e) {
32079                 this.dragZone.cachedTarget = null;
32080         var item = this.findItemFromChild(e.getTarget());
32081         if (!item) {
32082                 this.clearSelections(true);
32083                 return;
32084         }
32085                 if (item && (this.multiSelect || this.singleSelect)){
32086                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32087                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32088                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32089                                 this.unselect(item);
32090                         } else {
32091                                 this.select(item, this.multiSelect && e.ctrlKey);
32092                                 this.lastSelection = item;
32093                         }
32094                 }
32095     },
32096
32097     onItemClick : function(item, index, e){
32098                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32099                         return false;
32100                 }
32101                 return true;
32102     },
32103
32104     unselect : function(nodeInfo, suppressEvent){
32105                 var node = this.getNode(nodeInfo);
32106                 if(node && this.isSelected(node)){
32107                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32108                                 Roo.fly(node).removeClass(this.selectedClass);
32109                                 this.selections.remove(node);
32110                                 if(!suppressEvent){
32111                                         this.fireEvent("selectionchange", this, this.selections);
32112                                 }
32113                         }
32114                 }
32115     }
32116 });
32117 /*
32118  * Based on:
32119  * Ext JS Library 1.1.1
32120  * Copyright(c) 2006-2007, Ext JS, LLC.
32121  *
32122  * Originally Released Under LGPL - original licence link has changed is not relivant.
32123  *
32124  * Fork - LGPL
32125  * <script type="text/javascript">
32126  */
32127  
32128 /**
32129  * @class Roo.LayoutManager
32130  * @extends Roo.util.Observable
32131  * Base class for layout managers.
32132  */
32133 Roo.LayoutManager = function(container, config){
32134     Roo.LayoutManager.superclass.constructor.call(this);
32135     this.el = Roo.get(container);
32136     // ie scrollbar fix
32137     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32138         document.body.scroll = "no";
32139     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32140         this.el.position('relative');
32141     }
32142     this.id = this.el.id;
32143     this.el.addClass("x-layout-container");
32144     /** false to disable window resize monitoring @type Boolean */
32145     this.monitorWindowResize = true;
32146     this.regions = {};
32147     this.addEvents({
32148         /**
32149          * @event layout
32150          * Fires when a layout is performed. 
32151          * @param {Roo.LayoutManager} this
32152          */
32153         "layout" : true,
32154         /**
32155          * @event regionresized
32156          * Fires when the user resizes a region. 
32157          * @param {Roo.LayoutRegion} region The resized region
32158          * @param {Number} newSize The new size (width for east/west, height for north/south)
32159          */
32160         "regionresized" : true,
32161         /**
32162          * @event regioncollapsed
32163          * Fires when a region is collapsed. 
32164          * @param {Roo.LayoutRegion} region The collapsed region
32165          */
32166         "regioncollapsed" : true,
32167         /**
32168          * @event regionexpanded
32169          * Fires when a region is expanded.  
32170          * @param {Roo.LayoutRegion} region The expanded region
32171          */
32172         "regionexpanded" : true
32173     });
32174     this.updating = false;
32175     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32176 };
32177
32178 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32179     /**
32180      * Returns true if this layout is currently being updated
32181      * @return {Boolean}
32182      */
32183     isUpdating : function(){
32184         return this.updating; 
32185     },
32186     
32187     /**
32188      * Suspend the LayoutManager from doing auto-layouts while
32189      * making multiple add or remove calls
32190      */
32191     beginUpdate : function(){
32192         this.updating = true;    
32193     },
32194     
32195     /**
32196      * Restore auto-layouts and optionally disable the manager from performing a layout
32197      * @param {Boolean} noLayout true to disable a layout update 
32198      */
32199     endUpdate : function(noLayout){
32200         this.updating = false;
32201         if(!noLayout){
32202             this.layout();
32203         }    
32204     },
32205     
32206     layout: function(){
32207         
32208     },
32209     
32210     onRegionResized : function(region, newSize){
32211         this.fireEvent("regionresized", region, newSize);
32212         this.layout();
32213     },
32214     
32215     onRegionCollapsed : function(region){
32216         this.fireEvent("regioncollapsed", region);
32217     },
32218     
32219     onRegionExpanded : function(region){
32220         this.fireEvent("regionexpanded", region);
32221     },
32222         
32223     /**
32224      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32225      * performs box-model adjustments.
32226      * @return {Object} The size as an object {width: (the width), height: (the height)}
32227      */
32228     getViewSize : function(){
32229         var size;
32230         if(this.el.dom != document.body){
32231             size = this.el.getSize();
32232         }else{
32233             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32234         }
32235         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32236         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32237         return size;
32238     },
32239     
32240     /**
32241      * Returns the Element this layout is bound to.
32242      * @return {Roo.Element}
32243      */
32244     getEl : function(){
32245         return this.el;
32246     },
32247     
32248     /**
32249      * Returns the specified region.
32250      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32251      * @return {Roo.LayoutRegion}
32252      */
32253     getRegion : function(target){
32254         return this.regions[target.toLowerCase()];
32255     },
32256     
32257     onWindowResize : function(){
32258         if(this.monitorWindowResize){
32259             this.layout();
32260         }
32261     }
32262 });/*
32263  * Based on:
32264  * Ext JS Library 1.1.1
32265  * Copyright(c) 2006-2007, Ext JS, LLC.
32266  *
32267  * Originally Released Under LGPL - original licence link has changed is not relivant.
32268  *
32269  * Fork - LGPL
32270  * <script type="text/javascript">
32271  */
32272 /**
32273  * @class Roo.BorderLayout
32274  * @extends Roo.LayoutManager
32275  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32276  * please see: <br><br>
32277  * <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>
32278  * <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>
32279  * Example:
32280  <pre><code>
32281  var layout = new Roo.BorderLayout(document.body, {
32282     north: {
32283         initialSize: 25,
32284         titlebar: false
32285     },
32286     west: {
32287         split:true,
32288         initialSize: 200,
32289         minSize: 175,
32290         maxSize: 400,
32291         titlebar: true,
32292         collapsible: true
32293     },
32294     east: {
32295         split:true,
32296         initialSize: 202,
32297         minSize: 175,
32298         maxSize: 400,
32299         titlebar: true,
32300         collapsible: true
32301     },
32302     south: {
32303         split:true,
32304         initialSize: 100,
32305         minSize: 100,
32306         maxSize: 200,
32307         titlebar: true,
32308         collapsible: true
32309     },
32310     center: {
32311         titlebar: true,
32312         autoScroll:true,
32313         resizeTabs: true,
32314         minTabWidth: 50,
32315         preferredTabWidth: 150
32316     }
32317 });
32318
32319 // shorthand
32320 var CP = Roo.ContentPanel;
32321
32322 layout.beginUpdate();
32323 layout.add("north", new CP("north", "North"));
32324 layout.add("south", new CP("south", {title: "South", closable: true}));
32325 layout.add("west", new CP("west", {title: "West"}));
32326 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32327 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32328 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32329 layout.getRegion("center").showPanel("center1");
32330 layout.endUpdate();
32331 </code></pre>
32332
32333 <b>The container the layout is rendered into can be either the body element or any other element.
32334 If it is not the body element, the container needs to either be an absolute positioned element,
32335 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32336 the container size if it is not the body element.</b>
32337
32338 * @constructor
32339 * Create a new BorderLayout
32340 * @param {String/HTMLElement/Element} container The container this layout is bound to
32341 * @param {Object} config Configuration options
32342  */
32343 Roo.BorderLayout = function(container, config){
32344     config = config || {};
32345     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32346     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32347     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32348         var target = this.factory.validRegions[i];
32349         if(config[target]){
32350             this.addRegion(target, config[target]);
32351         }
32352     }
32353 };
32354
32355 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32356     /**
32357      * Creates and adds a new region if it doesn't already exist.
32358      * @param {String} target The target region key (north, south, east, west or center).
32359      * @param {Object} config The regions config object
32360      * @return {BorderLayoutRegion} The new region
32361      */
32362     addRegion : function(target, config){
32363         if(!this.regions[target]){
32364             var r = this.factory.create(target, this, config);
32365             this.bindRegion(target, r);
32366         }
32367         return this.regions[target];
32368     },
32369
32370     // private (kinda)
32371     bindRegion : function(name, r){
32372         this.regions[name] = r;
32373         r.on("visibilitychange", this.layout, this);
32374         r.on("paneladded", this.layout, this);
32375         r.on("panelremoved", this.layout, this);
32376         r.on("invalidated", this.layout, this);
32377         r.on("resized", this.onRegionResized, this);
32378         r.on("collapsed", this.onRegionCollapsed, this);
32379         r.on("expanded", this.onRegionExpanded, this);
32380     },
32381
32382     /**
32383      * Performs a layout update.
32384      */
32385     layout : function(){
32386         if(this.updating) return;
32387         var size = this.getViewSize();
32388         var w = size.width;
32389         var h = size.height;
32390         var centerW = w;
32391         var centerH = h;
32392         var centerY = 0;
32393         var centerX = 0;
32394         //var x = 0, y = 0;
32395
32396         var rs = this.regions;
32397         var north = rs["north"];
32398         var south = rs["south"]; 
32399         var west = rs["west"];
32400         var east = rs["east"];
32401         var center = rs["center"];
32402         //if(this.hideOnLayout){ // not supported anymore
32403             //c.el.setStyle("display", "none");
32404         //}
32405         if(north && north.isVisible()){
32406             var b = north.getBox();
32407             var m = north.getMargins();
32408             b.width = w - (m.left+m.right);
32409             b.x = m.left;
32410             b.y = m.top;
32411             centerY = b.height + b.y + m.bottom;
32412             centerH -= centerY;
32413             north.updateBox(this.safeBox(b));
32414         }
32415         if(south && south.isVisible()){
32416             var b = south.getBox();
32417             var m = south.getMargins();
32418             b.width = w - (m.left+m.right);
32419             b.x = m.left;
32420             var totalHeight = (b.height + m.top + m.bottom);
32421             b.y = h - totalHeight + m.top;
32422             centerH -= totalHeight;
32423             south.updateBox(this.safeBox(b));
32424         }
32425         if(west && west.isVisible()){
32426             var b = west.getBox();
32427             var m = west.getMargins();
32428             b.height = centerH - (m.top+m.bottom);
32429             b.x = m.left;
32430             b.y = centerY + m.top;
32431             var totalWidth = (b.width + m.left + m.right);
32432             centerX += totalWidth;
32433             centerW -= totalWidth;
32434             west.updateBox(this.safeBox(b));
32435         }
32436         if(east && east.isVisible()){
32437             var b = east.getBox();
32438             var m = east.getMargins();
32439             b.height = centerH - (m.top+m.bottom);
32440             var totalWidth = (b.width + m.left + m.right);
32441             b.x = w - totalWidth + m.left;
32442             b.y = centerY + m.top;
32443             centerW -= totalWidth;
32444             east.updateBox(this.safeBox(b));
32445         }
32446         if(center){
32447             var m = center.getMargins();
32448             var centerBox = {
32449                 x: centerX + m.left,
32450                 y: centerY + m.top,
32451                 width: centerW - (m.left+m.right),
32452                 height: centerH - (m.top+m.bottom)
32453             };
32454             //if(this.hideOnLayout){
32455                 //center.el.setStyle("display", "block");
32456             //}
32457             center.updateBox(this.safeBox(centerBox));
32458         }
32459         this.el.repaint();
32460         this.fireEvent("layout", this);
32461     },
32462
32463     // private
32464     safeBox : function(box){
32465         box.width = Math.max(0, box.width);
32466         box.height = Math.max(0, box.height);
32467         return box;
32468     },
32469
32470     /**
32471      * Adds a ContentPanel (or subclass) to this layout.
32472      * @param {String} target The target region key (north, south, east, west or center).
32473      * @param {Roo.ContentPanel} panel The panel to add
32474      * @return {Roo.ContentPanel} The added panel
32475      */
32476     add : function(target, panel){
32477          
32478         target = target.toLowerCase();
32479         return this.regions[target].add(panel);
32480     },
32481
32482     /**
32483      * Remove a ContentPanel (or subclass) to this layout.
32484      * @param {String} target The target region key (north, south, east, west or center).
32485      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32486      * @return {Roo.ContentPanel} The removed panel
32487      */
32488     remove : function(target, panel){
32489         target = target.toLowerCase();
32490         return this.regions[target].remove(panel);
32491     },
32492
32493     /**
32494      * Searches all regions for a panel with the specified id
32495      * @param {String} panelId
32496      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32497      */
32498     findPanel : function(panelId){
32499         var rs = this.regions;
32500         for(var target in rs){
32501             if(typeof rs[target] != "function"){
32502                 var p = rs[target].getPanel(panelId);
32503                 if(p){
32504                     return p;
32505                 }
32506             }
32507         }
32508         return null;
32509     },
32510
32511     /**
32512      * Searches all regions for a panel with the specified id and activates (shows) it.
32513      * @param {String/ContentPanel} panelId The panels id or the panel itself
32514      * @return {Roo.ContentPanel} The shown panel or null
32515      */
32516     showPanel : function(panelId) {
32517       var rs = this.regions;
32518       for(var target in rs){
32519          var r = rs[target];
32520          if(typeof r != "function"){
32521             if(r.hasPanel(panelId)){
32522                return r.showPanel(panelId);
32523             }
32524          }
32525       }
32526       return null;
32527    },
32528
32529    /**
32530      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32531      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32532      */
32533     restoreState : function(provider){
32534         if(!provider){
32535             provider = Roo.state.Manager;
32536         }
32537         var sm = new Roo.LayoutStateManager();
32538         sm.init(this, provider);
32539     },
32540
32541     /**
32542      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32543      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32544      * a valid ContentPanel config object.  Example:
32545      * <pre><code>
32546 // Create the main layout
32547 var layout = new Roo.BorderLayout('main-ct', {
32548     west: {
32549         split:true,
32550         minSize: 175,
32551         titlebar: true
32552     },
32553     center: {
32554         title:'Components'
32555     }
32556 }, 'main-ct');
32557
32558 // Create and add multiple ContentPanels at once via configs
32559 layout.batchAdd({
32560    west: {
32561        id: 'source-files',
32562        autoCreate:true,
32563        title:'Ext Source Files',
32564        autoScroll:true,
32565        fitToFrame:true
32566    },
32567    center : {
32568        el: cview,
32569        autoScroll:true,
32570        fitToFrame:true,
32571        toolbar: tb,
32572        resizeEl:'cbody'
32573    }
32574 });
32575 </code></pre>
32576      * @param {Object} regions An object containing ContentPanel configs by region name
32577      */
32578     batchAdd : function(regions){
32579         this.beginUpdate();
32580         for(var rname in regions){
32581             var lr = this.regions[rname];
32582             if(lr){
32583                 this.addTypedPanels(lr, regions[rname]);
32584             }
32585         }
32586         this.endUpdate();
32587     },
32588
32589     // private
32590     addTypedPanels : function(lr, ps){
32591         if(typeof ps == 'string'){
32592             lr.add(new Roo.ContentPanel(ps));
32593         }
32594         else if(ps instanceof Array){
32595             for(var i =0, len = ps.length; i < len; i++){
32596                 this.addTypedPanels(lr, ps[i]);
32597             }
32598         }
32599         else if(!ps.events){ // raw config?
32600             var el = ps.el;
32601             delete ps.el; // prevent conflict
32602             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32603         }
32604         else {  // panel object assumed!
32605             lr.add(ps);
32606         }
32607     },
32608     /**
32609      * Adds a xtype elements to the layout.
32610      * <pre><code>
32611
32612 layout.addxtype({
32613        xtype : 'ContentPanel',
32614        region: 'west',
32615        items: [ .... ]
32616    }
32617 );
32618
32619 layout.addxtype({
32620         xtype : 'NestedLayoutPanel',
32621         region: 'west',
32622         layout: {
32623            center: { },
32624            west: { }   
32625         },
32626         items : [ ... list of content panels or nested layout panels.. ]
32627    }
32628 );
32629 </code></pre>
32630      * @param {Object} cfg Xtype definition of item to add.
32631      */
32632     addxtype : function(cfg)
32633     {
32634         // basically accepts a pannel...
32635         // can accept a layout region..!?!?
32636         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32637         
32638         if (!cfg.xtype.match(/Panel$/)) {
32639             return false;
32640         }
32641         var ret = false;
32642         
32643         if (typeof(cfg.region) == 'undefined') {
32644             Roo.log("Failed to add Panel, region was not set");
32645             Roo.log(cfg);
32646             return false;
32647         }
32648         var region = cfg.region;
32649         delete cfg.region;
32650         
32651           
32652         var xitems = [];
32653         if (cfg.items) {
32654             xitems = cfg.items;
32655             delete cfg.items;
32656         }
32657         var nb = false;
32658         
32659         switch(cfg.xtype) 
32660         {
32661             case 'ContentPanel':  // ContentPanel (el, cfg)
32662             case 'ScrollPanel':  // ContentPanel (el, cfg)
32663             case 'ViewPanel': 
32664                 if(cfg.autoCreate) {
32665                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32666                 } else {
32667                     var el = this.el.createChild();
32668                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32669                 }
32670                 
32671                 this.add(region, ret);
32672                 break;
32673             
32674             
32675             case 'TreePanel': // our new panel!
32676                 cfg.el = this.el.createChild();
32677                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32678                 this.add(region, ret);
32679                 break;
32680             
32681             case 'NestedLayoutPanel': 
32682                 // create a new Layout (which is  a Border Layout...
32683                 var el = this.el.createChild();
32684                 var clayout = cfg.layout;
32685                 delete cfg.layout;
32686                 clayout.items   = clayout.items  || [];
32687                 // replace this exitems with the clayout ones..
32688                 xitems = clayout.items;
32689                  
32690                 
32691                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32692                     cfg.background = false;
32693                 }
32694                 var layout = new Roo.BorderLayout(el, clayout);
32695                 
32696                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32697                 //console.log('adding nested layout panel '  + cfg.toSource());
32698                 this.add(region, ret);
32699                 nb = {}; /// find first...
32700                 break;
32701                 
32702             case 'GridPanel': 
32703             
32704                 // needs grid and region
32705                 
32706                 //var el = this.getRegion(region).el.createChild();
32707                 var el = this.el.createChild();
32708                 // create the grid first...
32709                 
32710                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32711                 delete cfg.grid;
32712                 if (region == 'center' && this.active ) {
32713                     cfg.background = false;
32714                 }
32715                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32716                 
32717                 this.add(region, ret);
32718                 if (cfg.background) {
32719                     ret.on('activate', function(gp) {
32720                         if (!gp.grid.rendered) {
32721                             gp.grid.render();
32722                         }
32723                     });
32724                 } else {
32725                     grid.render();
32726                 }
32727                 break;
32728            
32729            
32730            
32731                 
32732                 
32733                 
32734             default:
32735                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32736                     
32737                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32738                     this.add(region, ret);
32739                 } else {
32740                 
32741                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32742                     return null;
32743                 }
32744                 
32745              // GridPanel (grid, cfg)
32746             
32747         }
32748         this.beginUpdate();
32749         // add children..
32750         var region = '';
32751         var abn = {};
32752         Roo.each(xitems, function(i)  {
32753             region = nb && i.region ? i.region : false;
32754             
32755             var add = ret.addxtype(i);
32756            
32757             if (region) {
32758                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32759                 if (!i.background) {
32760                     abn[region] = nb[region] ;
32761                 }
32762             }
32763             
32764         });
32765         this.endUpdate();
32766
32767         // make the last non-background panel active..
32768         //if (nb) { Roo.log(abn); }
32769         if (nb) {
32770             
32771             for(var r in abn) {
32772                 region = this.getRegion(r);
32773                 if (region) {
32774                     // tried using nb[r], but it does not work..
32775                      
32776                     region.showPanel(abn[r]);
32777                    
32778                 }
32779             }
32780         }
32781         return ret;
32782         
32783     }
32784 });
32785
32786 /**
32787  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32788  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32789  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32790  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32791  * <pre><code>
32792 // shorthand
32793 var CP = Roo.ContentPanel;
32794
32795 var layout = Roo.BorderLayout.create({
32796     north: {
32797         initialSize: 25,
32798         titlebar: false,
32799         panels: [new CP("north", "North")]
32800     },
32801     west: {
32802         split:true,
32803         initialSize: 200,
32804         minSize: 175,
32805         maxSize: 400,
32806         titlebar: true,
32807         collapsible: true,
32808         panels: [new CP("west", {title: "West"})]
32809     },
32810     east: {
32811         split:true,
32812         initialSize: 202,
32813         minSize: 175,
32814         maxSize: 400,
32815         titlebar: true,
32816         collapsible: true,
32817         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32818     },
32819     south: {
32820         split:true,
32821         initialSize: 100,
32822         minSize: 100,
32823         maxSize: 200,
32824         titlebar: true,
32825         collapsible: true,
32826         panels: [new CP("south", {title: "South", closable: true})]
32827     },
32828     center: {
32829         titlebar: true,
32830         autoScroll:true,
32831         resizeTabs: true,
32832         minTabWidth: 50,
32833         preferredTabWidth: 150,
32834         panels: [
32835             new CP("center1", {title: "Close Me", closable: true}),
32836             new CP("center2", {title: "Center Panel", closable: false})
32837         ]
32838     }
32839 }, document.body);
32840
32841 layout.getRegion("center").showPanel("center1");
32842 </code></pre>
32843  * @param config
32844  * @param targetEl
32845  */
32846 Roo.BorderLayout.create = function(config, targetEl){
32847     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32848     layout.beginUpdate();
32849     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32850     for(var j = 0, jlen = regions.length; j < jlen; j++){
32851         var lr = regions[j];
32852         if(layout.regions[lr] && config[lr].panels){
32853             var r = layout.regions[lr];
32854             var ps = config[lr].panels;
32855             layout.addTypedPanels(r, ps);
32856         }
32857     }
32858     layout.endUpdate();
32859     return layout;
32860 };
32861
32862 // private
32863 Roo.BorderLayout.RegionFactory = {
32864     // private
32865     validRegions : ["north","south","east","west","center"],
32866
32867     // private
32868     create : function(target, mgr, config){
32869         target = target.toLowerCase();
32870         if(config.lightweight || config.basic){
32871             return new Roo.BasicLayoutRegion(mgr, config, target);
32872         }
32873         switch(target){
32874             case "north":
32875                 return new Roo.NorthLayoutRegion(mgr, config);
32876             case "south":
32877                 return new Roo.SouthLayoutRegion(mgr, config);
32878             case "east":
32879                 return new Roo.EastLayoutRegion(mgr, config);
32880             case "west":
32881                 return new Roo.WestLayoutRegion(mgr, config);
32882             case "center":
32883                 return new Roo.CenterLayoutRegion(mgr, config);
32884         }
32885         throw 'Layout region "'+target+'" not supported.';
32886     }
32887 };/*
32888  * Based on:
32889  * Ext JS Library 1.1.1
32890  * Copyright(c) 2006-2007, Ext JS, LLC.
32891  *
32892  * Originally Released Under LGPL - original licence link has changed is not relivant.
32893  *
32894  * Fork - LGPL
32895  * <script type="text/javascript">
32896  */
32897  
32898 /**
32899  * @class Roo.BasicLayoutRegion
32900  * @extends Roo.util.Observable
32901  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32902  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32903  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32904  */
32905 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32906     this.mgr = mgr;
32907     this.position  = pos;
32908     this.events = {
32909         /**
32910          * @scope Roo.BasicLayoutRegion
32911          */
32912         
32913         /**
32914          * @event beforeremove
32915          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32916          * @param {Roo.LayoutRegion} this
32917          * @param {Roo.ContentPanel} panel The panel
32918          * @param {Object} e The cancel event object
32919          */
32920         "beforeremove" : true,
32921         /**
32922          * @event invalidated
32923          * Fires when the layout for this region is changed.
32924          * @param {Roo.LayoutRegion} this
32925          */
32926         "invalidated" : true,
32927         /**
32928          * @event visibilitychange
32929          * Fires when this region is shown or hidden 
32930          * @param {Roo.LayoutRegion} this
32931          * @param {Boolean} visibility true or false
32932          */
32933         "visibilitychange" : true,
32934         /**
32935          * @event paneladded
32936          * Fires when a panel is added. 
32937          * @param {Roo.LayoutRegion} this
32938          * @param {Roo.ContentPanel} panel The panel
32939          */
32940         "paneladded" : true,
32941         /**
32942          * @event panelremoved
32943          * Fires when a panel is removed. 
32944          * @param {Roo.LayoutRegion} this
32945          * @param {Roo.ContentPanel} panel The panel
32946          */
32947         "panelremoved" : true,
32948         /**
32949          * @event collapsed
32950          * Fires when this region is collapsed.
32951          * @param {Roo.LayoutRegion} this
32952          */
32953         "collapsed" : true,
32954         /**
32955          * @event expanded
32956          * Fires when this region is expanded.
32957          * @param {Roo.LayoutRegion} this
32958          */
32959         "expanded" : true,
32960         /**
32961          * @event slideshow
32962          * Fires when this region is slid into view.
32963          * @param {Roo.LayoutRegion} this
32964          */
32965         "slideshow" : true,
32966         /**
32967          * @event slidehide
32968          * Fires when this region slides out of view. 
32969          * @param {Roo.LayoutRegion} this
32970          */
32971         "slidehide" : true,
32972         /**
32973          * @event panelactivated
32974          * Fires when a panel is activated. 
32975          * @param {Roo.LayoutRegion} this
32976          * @param {Roo.ContentPanel} panel The activated panel
32977          */
32978         "panelactivated" : true,
32979         /**
32980          * @event resized
32981          * Fires when the user resizes this region. 
32982          * @param {Roo.LayoutRegion} this
32983          * @param {Number} newSize The new size (width for east/west, height for north/south)
32984          */
32985         "resized" : true
32986     };
32987     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32988     this.panels = new Roo.util.MixedCollection();
32989     this.panels.getKey = this.getPanelId.createDelegate(this);
32990     this.box = null;
32991     this.activePanel = null;
32992     // ensure listeners are added...
32993     
32994     if (config.listeners || config.events) {
32995         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
32996             listeners : config.listeners || {},
32997             events : config.events || {}
32998         });
32999     }
33000     
33001     if(skipConfig !== true){
33002         this.applyConfig(config);
33003     }
33004 };
33005
33006 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33007     getPanelId : function(p){
33008         return p.getId();
33009     },
33010     
33011     applyConfig : function(config){
33012         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33013         this.config = config;
33014         
33015     },
33016     
33017     /**
33018      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33019      * the width, for horizontal (north, south) the height.
33020      * @param {Number} newSize The new width or height
33021      */
33022     resizeTo : function(newSize){
33023         var el = this.el ? this.el :
33024                  (this.activePanel ? this.activePanel.getEl() : null);
33025         if(el){
33026             switch(this.position){
33027                 case "east":
33028                 case "west":
33029                     el.setWidth(newSize);
33030                     this.fireEvent("resized", this, newSize);
33031                 break;
33032                 case "north":
33033                 case "south":
33034                     el.setHeight(newSize);
33035                     this.fireEvent("resized", this, newSize);
33036                 break;                
33037             }
33038         }
33039     },
33040     
33041     getBox : function(){
33042         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33043     },
33044     
33045     getMargins : function(){
33046         return this.margins;
33047     },
33048     
33049     updateBox : function(box){
33050         this.box = box;
33051         var el = this.activePanel.getEl();
33052         el.dom.style.left = box.x + "px";
33053         el.dom.style.top = box.y + "px";
33054         this.activePanel.setSize(box.width, box.height);
33055     },
33056     
33057     /**
33058      * Returns the container element for this region.
33059      * @return {Roo.Element}
33060      */
33061     getEl : function(){
33062         return this.activePanel;
33063     },
33064     
33065     /**
33066      * Returns true if this region is currently visible.
33067      * @return {Boolean}
33068      */
33069     isVisible : function(){
33070         return this.activePanel ? true : false;
33071     },
33072     
33073     setActivePanel : function(panel){
33074         panel = this.getPanel(panel);
33075         if(this.activePanel && this.activePanel != panel){
33076             this.activePanel.setActiveState(false);
33077             this.activePanel.getEl().setLeftTop(-10000,-10000);
33078         }
33079         this.activePanel = panel;
33080         panel.setActiveState(true);
33081         if(this.box){
33082             panel.setSize(this.box.width, this.box.height);
33083         }
33084         this.fireEvent("panelactivated", this, panel);
33085         this.fireEvent("invalidated");
33086     },
33087     
33088     /**
33089      * Show the specified panel.
33090      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33091      * @return {Roo.ContentPanel} The shown panel or null
33092      */
33093     showPanel : function(panel){
33094         if(panel = this.getPanel(panel)){
33095             this.setActivePanel(panel);
33096         }
33097         return panel;
33098     },
33099     
33100     /**
33101      * Get the active panel for this region.
33102      * @return {Roo.ContentPanel} The active panel or null
33103      */
33104     getActivePanel : function(){
33105         return this.activePanel;
33106     },
33107     
33108     /**
33109      * Add the passed ContentPanel(s)
33110      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33111      * @return {Roo.ContentPanel} The panel added (if only one was added)
33112      */
33113     add : function(panel){
33114         if(arguments.length > 1){
33115             for(var i = 0, len = arguments.length; i < len; i++) {
33116                 this.add(arguments[i]);
33117             }
33118             return null;
33119         }
33120         if(this.hasPanel(panel)){
33121             this.showPanel(panel);
33122             return panel;
33123         }
33124         var el = panel.getEl();
33125         if(el.dom.parentNode != this.mgr.el.dom){
33126             this.mgr.el.dom.appendChild(el.dom);
33127         }
33128         if(panel.setRegion){
33129             panel.setRegion(this);
33130         }
33131         this.panels.add(panel);
33132         el.setStyle("position", "absolute");
33133         if(!panel.background){
33134             this.setActivePanel(panel);
33135             if(this.config.initialSize && this.panels.getCount()==1){
33136                 this.resizeTo(this.config.initialSize);
33137             }
33138         }
33139         this.fireEvent("paneladded", this, panel);
33140         return panel;
33141     },
33142     
33143     /**
33144      * Returns true if the panel is in this region.
33145      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33146      * @return {Boolean}
33147      */
33148     hasPanel : function(panel){
33149         if(typeof panel == "object"){ // must be panel obj
33150             panel = panel.getId();
33151         }
33152         return this.getPanel(panel) ? true : false;
33153     },
33154     
33155     /**
33156      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33157      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33158      * @param {Boolean} preservePanel Overrides the config preservePanel option
33159      * @return {Roo.ContentPanel} The panel that was removed
33160      */
33161     remove : function(panel, preservePanel){
33162         panel = this.getPanel(panel);
33163         if(!panel){
33164             return null;
33165         }
33166         var e = {};
33167         this.fireEvent("beforeremove", this, panel, e);
33168         if(e.cancel === true){
33169             return null;
33170         }
33171         var panelId = panel.getId();
33172         this.panels.removeKey(panelId);
33173         return panel;
33174     },
33175     
33176     /**
33177      * Returns the panel specified or null if it's not in this region.
33178      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33179      * @return {Roo.ContentPanel}
33180      */
33181     getPanel : function(id){
33182         if(typeof id == "object"){ // must be panel obj
33183             return id;
33184         }
33185         return this.panels.get(id);
33186     },
33187     
33188     /**
33189      * Returns this regions position (north/south/east/west/center).
33190      * @return {String} 
33191      */
33192     getPosition: function(){
33193         return this.position;    
33194     }
33195 });/*
33196  * Based on:
33197  * Ext JS Library 1.1.1
33198  * Copyright(c) 2006-2007, Ext JS, LLC.
33199  *
33200  * Originally Released Under LGPL - original licence link has changed is not relivant.
33201  *
33202  * Fork - LGPL
33203  * <script type="text/javascript">
33204  */
33205  
33206 /**
33207  * @class Roo.LayoutRegion
33208  * @extends Roo.BasicLayoutRegion
33209  * This class represents a region in a layout manager.
33210  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33211  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33212  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33213  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33214  * @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})
33215  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33216  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33217  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33218  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33219  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33220  * @cfg {String}    title           The title for the region (overrides panel titles)
33221  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33222  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33223  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33224  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33225  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33226  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33227  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33228  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33229  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33230  * @cfg {Boolean}   showPin         True to show a pin button
33231  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33232  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33233  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33234  * @cfg {Number}    width           For East/West panels
33235  * @cfg {Number}    height          For North/South panels
33236  * @cfg {Boolean}   split           To show the splitter
33237  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33238  */
33239 Roo.LayoutRegion = function(mgr, config, pos){
33240     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33241     var dh = Roo.DomHelper;
33242     /** This region's container element 
33243     * @type Roo.Element */
33244     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33245     /** This region's title element 
33246     * @type Roo.Element */
33247
33248     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33249         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33250         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33251     ]}, true);
33252     this.titleEl.enableDisplayMode();
33253     /** This region's title text element 
33254     * @type HTMLElement */
33255     this.titleTextEl = this.titleEl.dom.firstChild;
33256     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33257     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33258     this.closeBtn.enableDisplayMode();
33259     this.closeBtn.on("click", this.closeClicked, this);
33260     this.closeBtn.hide();
33261
33262     this.createBody(config);
33263     this.visible = true;
33264     this.collapsed = false;
33265
33266     if(config.hideWhenEmpty){
33267         this.hide();
33268         this.on("paneladded", this.validateVisibility, this);
33269         this.on("panelremoved", this.validateVisibility, this);
33270     }
33271     this.applyConfig(config);
33272 };
33273
33274 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33275
33276     createBody : function(){
33277         /** This region's body element 
33278         * @type Roo.Element */
33279         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33280     },
33281
33282     applyConfig : function(c){
33283         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33284             var dh = Roo.DomHelper;
33285             if(c.titlebar !== false){
33286                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33287                 this.collapseBtn.on("click", this.collapse, this);
33288                 this.collapseBtn.enableDisplayMode();
33289
33290                 if(c.showPin === true || this.showPin){
33291                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33292                     this.stickBtn.enableDisplayMode();
33293                     this.stickBtn.on("click", this.expand, this);
33294                     this.stickBtn.hide();
33295                 }
33296             }
33297             /** This region's collapsed element
33298             * @type Roo.Element */
33299             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33300                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33301             ]}, true);
33302             if(c.floatable !== false){
33303                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33304                this.collapsedEl.on("click", this.collapseClick, this);
33305             }
33306
33307             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33308                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33309                    id: "message", unselectable: "on", style:{"float":"left"}});
33310                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33311              }
33312             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33313             this.expandBtn.on("click", this.expand, this);
33314         }
33315         if(this.collapseBtn){
33316             this.collapseBtn.setVisible(c.collapsible == true);
33317         }
33318         this.cmargins = c.cmargins || this.cmargins ||
33319                          (this.position == "west" || this.position == "east" ?
33320                              {top: 0, left: 2, right:2, bottom: 0} :
33321                              {top: 2, left: 0, right:0, bottom: 2});
33322         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33323         this.bottomTabs = c.tabPosition != "top";
33324         this.autoScroll = c.autoScroll || false;
33325         if(this.autoScroll){
33326             this.bodyEl.setStyle("overflow", "auto");
33327         }else{
33328             this.bodyEl.setStyle("overflow", "hidden");
33329         }
33330         //if(c.titlebar !== false){
33331             if((!c.titlebar && !c.title) || c.titlebar === false){
33332                 this.titleEl.hide();
33333             }else{
33334                 this.titleEl.show();
33335                 if(c.title){
33336                     this.titleTextEl.innerHTML = c.title;
33337                 }
33338             }
33339         //}
33340         this.duration = c.duration || .30;
33341         this.slideDuration = c.slideDuration || .45;
33342         this.config = c;
33343         if(c.collapsed){
33344             this.collapse(true);
33345         }
33346         if(c.hidden){
33347             this.hide();
33348         }
33349     },
33350     /**
33351      * Returns true if this region is currently visible.
33352      * @return {Boolean}
33353      */
33354     isVisible : function(){
33355         return this.visible;
33356     },
33357
33358     /**
33359      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33360      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33361      */
33362     setCollapsedTitle : function(title){
33363         title = title || "&#160;";
33364         if(this.collapsedTitleTextEl){
33365             this.collapsedTitleTextEl.innerHTML = title;
33366         }
33367     },
33368
33369     getBox : function(){
33370         var b;
33371         if(!this.collapsed){
33372             b = this.el.getBox(false, true);
33373         }else{
33374             b = this.collapsedEl.getBox(false, true);
33375         }
33376         return b;
33377     },
33378
33379     getMargins : function(){
33380         return this.collapsed ? this.cmargins : this.margins;
33381     },
33382
33383     highlight : function(){
33384         this.el.addClass("x-layout-panel-dragover");
33385     },
33386
33387     unhighlight : function(){
33388         this.el.removeClass("x-layout-panel-dragover");
33389     },
33390
33391     updateBox : function(box){
33392         this.box = box;
33393         if(!this.collapsed){
33394             this.el.dom.style.left = box.x + "px";
33395             this.el.dom.style.top = box.y + "px";
33396             this.updateBody(box.width, box.height);
33397         }else{
33398             this.collapsedEl.dom.style.left = box.x + "px";
33399             this.collapsedEl.dom.style.top = box.y + "px";
33400             this.collapsedEl.setSize(box.width, box.height);
33401         }
33402         if(this.tabs){
33403             this.tabs.autoSizeTabs();
33404         }
33405     },
33406
33407     updateBody : function(w, h){
33408         if(w !== null){
33409             this.el.setWidth(w);
33410             w -= this.el.getBorderWidth("rl");
33411             if(this.config.adjustments){
33412                 w += this.config.adjustments[0];
33413             }
33414         }
33415         if(h !== null){
33416             this.el.setHeight(h);
33417             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33418             h -= this.el.getBorderWidth("tb");
33419             if(this.config.adjustments){
33420                 h += this.config.adjustments[1];
33421             }
33422             this.bodyEl.setHeight(h);
33423             if(this.tabs){
33424                 h = this.tabs.syncHeight(h);
33425             }
33426         }
33427         if(this.panelSize){
33428             w = w !== null ? w : this.panelSize.width;
33429             h = h !== null ? h : this.panelSize.height;
33430         }
33431         if(this.activePanel){
33432             var el = this.activePanel.getEl();
33433             w = w !== null ? w : el.getWidth();
33434             h = h !== null ? h : el.getHeight();
33435             this.panelSize = {width: w, height: h};
33436             this.activePanel.setSize(w, h);
33437         }
33438         if(Roo.isIE && this.tabs){
33439             this.tabs.el.repaint();
33440         }
33441     },
33442
33443     /**
33444      * Returns the container element for this region.
33445      * @return {Roo.Element}
33446      */
33447     getEl : function(){
33448         return this.el;
33449     },
33450
33451     /**
33452      * Hides this region.
33453      */
33454     hide : function(){
33455         if(!this.collapsed){
33456             this.el.dom.style.left = "-2000px";
33457             this.el.hide();
33458         }else{
33459             this.collapsedEl.dom.style.left = "-2000px";
33460             this.collapsedEl.hide();
33461         }
33462         this.visible = false;
33463         this.fireEvent("visibilitychange", this, false);
33464     },
33465
33466     /**
33467      * Shows this region if it was previously hidden.
33468      */
33469     show : function(){
33470         if(!this.collapsed){
33471             this.el.show();
33472         }else{
33473             this.collapsedEl.show();
33474         }
33475         this.visible = true;
33476         this.fireEvent("visibilitychange", this, true);
33477     },
33478
33479     closeClicked : function(){
33480         if(this.activePanel){
33481             this.remove(this.activePanel);
33482         }
33483     },
33484
33485     collapseClick : function(e){
33486         if(this.isSlid){
33487            e.stopPropagation();
33488            this.slideIn();
33489         }else{
33490            e.stopPropagation();
33491            this.slideOut();
33492         }
33493     },
33494
33495     /**
33496      * Collapses this region.
33497      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33498      */
33499     collapse : function(skipAnim){
33500         if(this.collapsed) return;
33501         this.collapsed = true;
33502         if(this.split){
33503             this.split.el.hide();
33504         }
33505         if(this.config.animate && skipAnim !== true){
33506             this.fireEvent("invalidated", this);
33507             this.animateCollapse();
33508         }else{
33509             this.el.setLocation(-20000,-20000);
33510             this.el.hide();
33511             this.collapsedEl.show();
33512             this.fireEvent("collapsed", this);
33513             this.fireEvent("invalidated", this);
33514         }
33515     },
33516
33517     animateCollapse : function(){
33518         // overridden
33519     },
33520
33521     /**
33522      * Expands this region if it was previously collapsed.
33523      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33524      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33525      */
33526     expand : function(e, skipAnim){
33527         if(e) e.stopPropagation();
33528         if(!this.collapsed || this.el.hasActiveFx()) return;
33529         if(this.isSlid){
33530             this.afterSlideIn();
33531             skipAnim = true;
33532         }
33533         this.collapsed = false;
33534         if(this.config.animate && skipAnim !== true){
33535             this.animateExpand();
33536         }else{
33537             this.el.show();
33538             if(this.split){
33539                 this.split.el.show();
33540             }
33541             this.collapsedEl.setLocation(-2000,-2000);
33542             this.collapsedEl.hide();
33543             this.fireEvent("invalidated", this);
33544             this.fireEvent("expanded", this);
33545         }
33546     },
33547
33548     animateExpand : function(){
33549         // overridden
33550     },
33551
33552     initTabs : function()
33553     {
33554         this.bodyEl.setStyle("overflow", "hidden");
33555         var ts = new Roo.TabPanel(
33556                 this.bodyEl.dom,
33557                 {
33558                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33559                     disableTooltips: this.config.disableTabTips,
33560                     toolbar : this.config.toolbar
33561                 }
33562         );
33563         if(this.config.hideTabs){
33564             ts.stripWrap.setDisplayed(false);
33565         }
33566         this.tabs = ts;
33567         ts.resizeTabs = this.config.resizeTabs === true;
33568         ts.minTabWidth = this.config.minTabWidth || 40;
33569         ts.maxTabWidth = this.config.maxTabWidth || 250;
33570         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33571         ts.monitorResize = false;
33572         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33573         ts.bodyEl.addClass('x-layout-tabs-body');
33574         this.panels.each(this.initPanelAsTab, this);
33575     },
33576
33577     initPanelAsTab : function(panel){
33578         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33579                     this.config.closeOnTab && panel.isClosable());
33580         if(panel.tabTip !== undefined){
33581             ti.setTooltip(panel.tabTip);
33582         }
33583         ti.on("activate", function(){
33584               this.setActivePanel(panel);
33585         }, this);
33586         if(this.config.closeOnTab){
33587             ti.on("beforeclose", function(t, e){
33588                 e.cancel = true;
33589                 this.remove(panel);
33590             }, this);
33591         }
33592         return ti;
33593     },
33594
33595     updatePanelTitle : function(panel, title){
33596         if(this.activePanel == panel){
33597             this.updateTitle(title);
33598         }
33599         if(this.tabs){
33600             var ti = this.tabs.getTab(panel.getEl().id);
33601             ti.setText(title);
33602             if(panel.tabTip !== undefined){
33603                 ti.setTooltip(panel.tabTip);
33604             }
33605         }
33606     },
33607
33608     updateTitle : function(title){
33609         if(this.titleTextEl && !this.config.title){
33610             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33611         }
33612     },
33613
33614     setActivePanel : function(panel){
33615         panel = this.getPanel(panel);
33616         if(this.activePanel && this.activePanel != panel){
33617             this.activePanel.setActiveState(false);
33618         }
33619         this.activePanel = panel;
33620         panel.setActiveState(true);
33621         if(this.panelSize){
33622             panel.setSize(this.panelSize.width, this.panelSize.height);
33623         }
33624         if(this.closeBtn){
33625             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33626         }
33627         this.updateTitle(panel.getTitle());
33628         if(this.tabs){
33629             this.fireEvent("invalidated", this);
33630         }
33631         this.fireEvent("panelactivated", this, panel);
33632     },
33633
33634     /**
33635      * Shows the specified panel.
33636      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33637      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33638      */
33639     showPanel : function(panel){
33640         if(panel = this.getPanel(panel)){
33641             if(this.tabs){
33642                 var tab = this.tabs.getTab(panel.getEl().id);
33643                 if(tab.isHidden()){
33644                     this.tabs.unhideTab(tab.id);
33645                 }
33646                 tab.activate();
33647             }else{
33648                 this.setActivePanel(panel);
33649             }
33650         }
33651         return panel;
33652     },
33653
33654     /**
33655      * Get the active panel for this region.
33656      * @return {Roo.ContentPanel} The active panel or null
33657      */
33658     getActivePanel : function(){
33659         return this.activePanel;
33660     },
33661
33662     validateVisibility : function(){
33663         if(this.panels.getCount() < 1){
33664             this.updateTitle("&#160;");
33665             this.closeBtn.hide();
33666             this.hide();
33667         }else{
33668             if(!this.isVisible()){
33669                 this.show();
33670             }
33671         }
33672     },
33673
33674     /**
33675      * Adds the passed ContentPanel(s) to this region.
33676      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33677      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33678      */
33679     add : function(panel){
33680         if(arguments.length > 1){
33681             for(var i = 0, len = arguments.length; i < len; i++) {
33682                 this.add(arguments[i]);
33683             }
33684             return null;
33685         }
33686         if(this.hasPanel(panel)){
33687             this.showPanel(panel);
33688             return panel;
33689         }
33690         panel.setRegion(this);
33691         this.panels.add(panel);
33692         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33693             this.bodyEl.dom.appendChild(panel.getEl().dom);
33694             if(panel.background !== true){
33695                 this.setActivePanel(panel);
33696             }
33697             this.fireEvent("paneladded", this, panel);
33698             return panel;
33699         }
33700         if(!this.tabs){
33701             this.initTabs();
33702         }else{
33703             this.initPanelAsTab(panel);
33704         }
33705         if(panel.background !== true){
33706             this.tabs.activate(panel.getEl().id);
33707         }
33708         this.fireEvent("paneladded", this, panel);
33709         return panel;
33710     },
33711
33712     /**
33713      * Hides the tab for the specified panel.
33714      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33715      */
33716     hidePanel : function(panel){
33717         if(this.tabs && (panel = this.getPanel(panel))){
33718             this.tabs.hideTab(panel.getEl().id);
33719         }
33720     },
33721
33722     /**
33723      * Unhides the tab for a previously hidden panel.
33724      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33725      */
33726     unhidePanel : function(panel){
33727         if(this.tabs && (panel = this.getPanel(panel))){
33728             this.tabs.unhideTab(panel.getEl().id);
33729         }
33730     },
33731
33732     clearPanels : function(){
33733         while(this.panels.getCount() > 0){
33734              this.remove(this.panels.first());
33735         }
33736     },
33737
33738     /**
33739      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33740      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33741      * @param {Boolean} preservePanel Overrides the config preservePanel option
33742      * @return {Roo.ContentPanel} The panel that was removed
33743      */
33744     remove : function(panel, preservePanel){
33745         panel = this.getPanel(panel);
33746         if(!panel){
33747             return null;
33748         }
33749         var e = {};
33750         this.fireEvent("beforeremove", this, panel, e);
33751         if(e.cancel === true){
33752             return null;
33753         }
33754         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33755         var panelId = panel.getId();
33756         this.panels.removeKey(panelId);
33757         if(preservePanel){
33758             document.body.appendChild(panel.getEl().dom);
33759         }
33760         if(this.tabs){
33761             this.tabs.removeTab(panel.getEl().id);
33762         }else if (!preservePanel){
33763             this.bodyEl.dom.removeChild(panel.getEl().dom);
33764         }
33765         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33766             var p = this.panels.first();
33767             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33768             tempEl.appendChild(p.getEl().dom);
33769             this.bodyEl.update("");
33770             this.bodyEl.dom.appendChild(p.getEl().dom);
33771             tempEl = null;
33772             this.updateTitle(p.getTitle());
33773             this.tabs = null;
33774             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33775             this.setActivePanel(p);
33776         }
33777         panel.setRegion(null);
33778         if(this.activePanel == panel){
33779             this.activePanel = null;
33780         }
33781         if(this.config.autoDestroy !== false && preservePanel !== true){
33782             try{panel.destroy();}catch(e){}
33783         }
33784         this.fireEvent("panelremoved", this, panel);
33785         return panel;
33786     },
33787
33788     /**
33789      * Returns the TabPanel component used by this region
33790      * @return {Roo.TabPanel}
33791      */
33792     getTabs : function(){
33793         return this.tabs;
33794     },
33795
33796     createTool : function(parentEl, className){
33797         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33798             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33799         btn.addClassOnOver("x-layout-tools-button-over");
33800         return btn;
33801     }
33802 });/*
33803  * Based on:
33804  * Ext JS Library 1.1.1
33805  * Copyright(c) 2006-2007, Ext JS, LLC.
33806  *
33807  * Originally Released Under LGPL - original licence link has changed is not relivant.
33808  *
33809  * Fork - LGPL
33810  * <script type="text/javascript">
33811  */
33812  
33813
33814
33815 /**
33816  * @class Roo.SplitLayoutRegion
33817  * @extends Roo.LayoutRegion
33818  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33819  */
33820 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33821     this.cursor = cursor;
33822     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33823 };
33824
33825 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33826     splitTip : "Drag to resize.",
33827     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33828     useSplitTips : false,
33829
33830     applyConfig : function(config){
33831         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33832         if(config.split){
33833             if(!this.split){
33834                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33835                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33836                 /** The SplitBar for this region 
33837                 * @type Roo.SplitBar */
33838                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33839                 this.split.on("moved", this.onSplitMove, this);
33840                 this.split.useShim = config.useShim === true;
33841                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33842                 if(this.useSplitTips){
33843                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33844                 }
33845                 if(config.collapsible){
33846                     this.split.el.on("dblclick", this.collapse,  this);
33847                 }
33848             }
33849             if(typeof config.minSize != "undefined"){
33850                 this.split.minSize = config.minSize;
33851             }
33852             if(typeof config.maxSize != "undefined"){
33853                 this.split.maxSize = config.maxSize;
33854             }
33855             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33856                 this.hideSplitter();
33857             }
33858         }
33859     },
33860
33861     getHMaxSize : function(){
33862          var cmax = this.config.maxSize || 10000;
33863          var center = this.mgr.getRegion("center");
33864          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33865     },
33866
33867     getVMaxSize : function(){
33868          var cmax = this.config.maxSize || 10000;
33869          var center = this.mgr.getRegion("center");
33870          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33871     },
33872
33873     onSplitMove : function(split, newSize){
33874         this.fireEvent("resized", this, newSize);
33875     },
33876     
33877     /** 
33878      * Returns the {@link Roo.SplitBar} for this region.
33879      * @return {Roo.SplitBar}
33880      */
33881     getSplitBar : function(){
33882         return this.split;
33883     },
33884     
33885     hide : function(){
33886         this.hideSplitter();
33887         Roo.SplitLayoutRegion.superclass.hide.call(this);
33888     },
33889
33890     hideSplitter : function(){
33891         if(this.split){
33892             this.split.el.setLocation(-2000,-2000);
33893             this.split.el.hide();
33894         }
33895     },
33896
33897     show : function(){
33898         if(this.split){
33899             this.split.el.show();
33900         }
33901         Roo.SplitLayoutRegion.superclass.show.call(this);
33902     },
33903     
33904     beforeSlide: function(){
33905         if(Roo.isGecko){// firefox overflow auto bug workaround
33906             this.bodyEl.clip();
33907             if(this.tabs) this.tabs.bodyEl.clip();
33908             if(this.activePanel){
33909                 this.activePanel.getEl().clip();
33910                 
33911                 if(this.activePanel.beforeSlide){
33912                     this.activePanel.beforeSlide();
33913                 }
33914             }
33915         }
33916     },
33917     
33918     afterSlide : function(){
33919         if(Roo.isGecko){// firefox overflow auto bug workaround
33920             this.bodyEl.unclip();
33921             if(this.tabs) this.tabs.bodyEl.unclip();
33922             if(this.activePanel){
33923                 this.activePanel.getEl().unclip();
33924                 if(this.activePanel.afterSlide){
33925                     this.activePanel.afterSlide();
33926                 }
33927             }
33928         }
33929     },
33930
33931     initAutoHide : function(){
33932         if(this.autoHide !== false){
33933             if(!this.autoHideHd){
33934                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33935                 this.autoHideHd = {
33936                     "mouseout": function(e){
33937                         if(!e.within(this.el, true)){
33938                             st.delay(500);
33939                         }
33940                     },
33941                     "mouseover" : function(e){
33942                         st.cancel();
33943                     },
33944                     scope : this
33945                 };
33946             }
33947             this.el.on(this.autoHideHd);
33948         }
33949     },
33950
33951     clearAutoHide : function(){
33952         if(this.autoHide !== false){
33953             this.el.un("mouseout", this.autoHideHd.mouseout);
33954             this.el.un("mouseover", this.autoHideHd.mouseover);
33955         }
33956     },
33957
33958     clearMonitor : function(){
33959         Roo.get(document).un("click", this.slideInIf, this);
33960     },
33961
33962     // these names are backwards but not changed for compat
33963     slideOut : function(){
33964         if(this.isSlid || this.el.hasActiveFx()){
33965             return;
33966         }
33967         this.isSlid = true;
33968         if(this.collapseBtn){
33969             this.collapseBtn.hide();
33970         }
33971         this.closeBtnState = this.closeBtn.getStyle('display');
33972         this.closeBtn.hide();
33973         if(this.stickBtn){
33974             this.stickBtn.show();
33975         }
33976         this.el.show();
33977         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33978         this.beforeSlide();
33979         this.el.setStyle("z-index", 10001);
33980         this.el.slideIn(this.getSlideAnchor(), {
33981             callback: function(){
33982                 this.afterSlide();
33983                 this.initAutoHide();
33984                 Roo.get(document).on("click", this.slideInIf, this);
33985                 this.fireEvent("slideshow", this);
33986             },
33987             scope: this,
33988             block: true
33989         });
33990     },
33991
33992     afterSlideIn : function(){
33993         this.clearAutoHide();
33994         this.isSlid = false;
33995         this.clearMonitor();
33996         this.el.setStyle("z-index", "");
33997         if(this.collapseBtn){
33998             this.collapseBtn.show();
33999         }
34000         this.closeBtn.setStyle('display', this.closeBtnState);
34001         if(this.stickBtn){
34002             this.stickBtn.hide();
34003         }
34004         this.fireEvent("slidehide", this);
34005     },
34006
34007     slideIn : function(cb){
34008         if(!this.isSlid || this.el.hasActiveFx()){
34009             Roo.callback(cb);
34010             return;
34011         }
34012         this.isSlid = false;
34013         this.beforeSlide();
34014         this.el.slideOut(this.getSlideAnchor(), {
34015             callback: function(){
34016                 this.el.setLeftTop(-10000, -10000);
34017                 this.afterSlide();
34018                 this.afterSlideIn();
34019                 Roo.callback(cb);
34020             },
34021             scope: this,
34022             block: true
34023         });
34024     },
34025     
34026     slideInIf : function(e){
34027         if(!e.within(this.el)){
34028             this.slideIn();
34029         }
34030     },
34031
34032     animateCollapse : function(){
34033         this.beforeSlide();
34034         this.el.setStyle("z-index", 20000);
34035         var anchor = this.getSlideAnchor();
34036         this.el.slideOut(anchor, {
34037             callback : function(){
34038                 this.el.setStyle("z-index", "");
34039                 this.collapsedEl.slideIn(anchor, {duration:.3});
34040                 this.afterSlide();
34041                 this.el.setLocation(-10000,-10000);
34042                 this.el.hide();
34043                 this.fireEvent("collapsed", this);
34044             },
34045             scope: this,
34046             block: true
34047         });
34048     },
34049
34050     animateExpand : function(){
34051         this.beforeSlide();
34052         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34053         this.el.setStyle("z-index", 20000);
34054         this.collapsedEl.hide({
34055             duration:.1
34056         });
34057         this.el.slideIn(this.getSlideAnchor(), {
34058             callback : function(){
34059                 this.el.setStyle("z-index", "");
34060                 this.afterSlide();
34061                 if(this.split){
34062                     this.split.el.show();
34063                 }
34064                 this.fireEvent("invalidated", this);
34065                 this.fireEvent("expanded", this);
34066             },
34067             scope: this,
34068             block: true
34069         });
34070     },
34071
34072     anchors : {
34073         "west" : "left",
34074         "east" : "right",
34075         "north" : "top",
34076         "south" : "bottom"
34077     },
34078
34079     sanchors : {
34080         "west" : "l",
34081         "east" : "r",
34082         "north" : "t",
34083         "south" : "b"
34084     },
34085
34086     canchors : {
34087         "west" : "tl-tr",
34088         "east" : "tr-tl",
34089         "north" : "tl-bl",
34090         "south" : "bl-tl"
34091     },
34092
34093     getAnchor : function(){
34094         return this.anchors[this.position];
34095     },
34096
34097     getCollapseAnchor : function(){
34098         return this.canchors[this.position];
34099     },
34100
34101     getSlideAnchor : function(){
34102         return this.sanchors[this.position];
34103     },
34104
34105     getAlignAdj : function(){
34106         var cm = this.cmargins;
34107         switch(this.position){
34108             case "west":
34109                 return [0, 0];
34110             break;
34111             case "east":
34112                 return [0, 0];
34113             break;
34114             case "north":
34115                 return [0, 0];
34116             break;
34117             case "south":
34118                 return [0, 0];
34119             break;
34120         }
34121     },
34122
34123     getExpandAdj : function(){
34124         var c = this.collapsedEl, cm = this.cmargins;
34125         switch(this.position){
34126             case "west":
34127                 return [-(cm.right+c.getWidth()+cm.left), 0];
34128             break;
34129             case "east":
34130                 return [cm.right+c.getWidth()+cm.left, 0];
34131             break;
34132             case "north":
34133                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34134             break;
34135             case "south":
34136                 return [0, cm.top+cm.bottom+c.getHeight()];
34137             break;
34138         }
34139     }
34140 });/*
34141  * Based on:
34142  * Ext JS Library 1.1.1
34143  * Copyright(c) 2006-2007, Ext JS, LLC.
34144  *
34145  * Originally Released Under LGPL - original licence link has changed is not relivant.
34146  *
34147  * Fork - LGPL
34148  * <script type="text/javascript">
34149  */
34150 /*
34151  * These classes are private internal classes
34152  */
34153 Roo.CenterLayoutRegion = function(mgr, config){
34154     Roo.LayoutRegion.call(this, mgr, config, "center");
34155     this.visible = true;
34156     this.minWidth = config.minWidth || 20;
34157     this.minHeight = config.minHeight || 20;
34158 };
34159
34160 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34161     hide : function(){
34162         // center panel can't be hidden
34163     },
34164     
34165     show : function(){
34166         // center panel can't be hidden
34167     },
34168     
34169     getMinWidth: function(){
34170         return this.minWidth;
34171     },
34172     
34173     getMinHeight: function(){
34174         return this.minHeight;
34175     }
34176 });
34177
34178
34179 Roo.NorthLayoutRegion = function(mgr, config){
34180     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34181     if(this.split){
34182         this.split.placement = Roo.SplitBar.TOP;
34183         this.split.orientation = Roo.SplitBar.VERTICAL;
34184         this.split.el.addClass("x-layout-split-v");
34185     }
34186     var size = config.initialSize || config.height;
34187     if(typeof size != "undefined"){
34188         this.el.setHeight(size);
34189     }
34190 };
34191 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34192     orientation: Roo.SplitBar.VERTICAL,
34193     getBox : function(){
34194         if(this.collapsed){
34195             return this.collapsedEl.getBox();
34196         }
34197         var box = this.el.getBox();
34198         if(this.split){
34199             box.height += this.split.el.getHeight();
34200         }
34201         return box;
34202     },
34203     
34204     updateBox : function(box){
34205         if(this.split && !this.collapsed){
34206             box.height -= this.split.el.getHeight();
34207             this.split.el.setLeft(box.x);
34208             this.split.el.setTop(box.y+box.height);
34209             this.split.el.setWidth(box.width);
34210         }
34211         if(this.collapsed){
34212             this.updateBody(box.width, null);
34213         }
34214         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34215     }
34216 });
34217
34218 Roo.SouthLayoutRegion = function(mgr, config){
34219     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34220     if(this.split){
34221         this.split.placement = Roo.SplitBar.BOTTOM;
34222         this.split.orientation = Roo.SplitBar.VERTICAL;
34223         this.split.el.addClass("x-layout-split-v");
34224     }
34225     var size = config.initialSize || config.height;
34226     if(typeof size != "undefined"){
34227         this.el.setHeight(size);
34228     }
34229 };
34230 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34231     orientation: Roo.SplitBar.VERTICAL,
34232     getBox : function(){
34233         if(this.collapsed){
34234             return this.collapsedEl.getBox();
34235         }
34236         var box = this.el.getBox();
34237         if(this.split){
34238             var sh = this.split.el.getHeight();
34239             box.height += sh;
34240             box.y -= sh;
34241         }
34242         return box;
34243     },
34244     
34245     updateBox : function(box){
34246         if(this.split && !this.collapsed){
34247             var sh = this.split.el.getHeight();
34248             box.height -= sh;
34249             box.y += sh;
34250             this.split.el.setLeft(box.x);
34251             this.split.el.setTop(box.y-sh);
34252             this.split.el.setWidth(box.width);
34253         }
34254         if(this.collapsed){
34255             this.updateBody(box.width, null);
34256         }
34257         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34258     }
34259 });
34260
34261 Roo.EastLayoutRegion = function(mgr, config){
34262     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34263     if(this.split){
34264         this.split.placement = Roo.SplitBar.RIGHT;
34265         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34266         this.split.el.addClass("x-layout-split-h");
34267     }
34268     var size = config.initialSize || config.width;
34269     if(typeof size != "undefined"){
34270         this.el.setWidth(size);
34271     }
34272 };
34273 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34274     orientation: Roo.SplitBar.HORIZONTAL,
34275     getBox : function(){
34276         if(this.collapsed){
34277             return this.collapsedEl.getBox();
34278         }
34279         var box = this.el.getBox();
34280         if(this.split){
34281             var sw = this.split.el.getWidth();
34282             box.width += sw;
34283             box.x -= sw;
34284         }
34285         return box;
34286     },
34287
34288     updateBox : function(box){
34289         if(this.split && !this.collapsed){
34290             var sw = this.split.el.getWidth();
34291             box.width -= sw;
34292             this.split.el.setLeft(box.x);
34293             this.split.el.setTop(box.y);
34294             this.split.el.setHeight(box.height);
34295             box.x += sw;
34296         }
34297         if(this.collapsed){
34298             this.updateBody(null, box.height);
34299         }
34300         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34301     }
34302 });
34303
34304 Roo.WestLayoutRegion = function(mgr, config){
34305     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34306     if(this.split){
34307         this.split.placement = Roo.SplitBar.LEFT;
34308         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34309         this.split.el.addClass("x-layout-split-h");
34310     }
34311     var size = config.initialSize || config.width;
34312     if(typeof size != "undefined"){
34313         this.el.setWidth(size);
34314     }
34315 };
34316 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34317     orientation: Roo.SplitBar.HORIZONTAL,
34318     getBox : function(){
34319         if(this.collapsed){
34320             return this.collapsedEl.getBox();
34321         }
34322         var box = this.el.getBox();
34323         if(this.split){
34324             box.width += this.split.el.getWidth();
34325         }
34326         return box;
34327     },
34328     
34329     updateBox : function(box){
34330         if(this.split && !this.collapsed){
34331             var sw = this.split.el.getWidth();
34332             box.width -= sw;
34333             this.split.el.setLeft(box.x+box.width);
34334             this.split.el.setTop(box.y);
34335             this.split.el.setHeight(box.height);
34336         }
34337         if(this.collapsed){
34338             this.updateBody(null, box.height);
34339         }
34340         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34341     }
34342 });
34343 /*
34344  * Based on:
34345  * Ext JS Library 1.1.1
34346  * Copyright(c) 2006-2007, Ext JS, LLC.
34347  *
34348  * Originally Released Under LGPL - original licence link has changed is not relivant.
34349  *
34350  * Fork - LGPL
34351  * <script type="text/javascript">
34352  */
34353  
34354  
34355 /*
34356  * Private internal class for reading and applying state
34357  */
34358 Roo.LayoutStateManager = function(layout){
34359      // default empty state
34360      this.state = {
34361         north: {},
34362         south: {},
34363         east: {},
34364         west: {}       
34365     };
34366 };
34367
34368 Roo.LayoutStateManager.prototype = {
34369     init : function(layout, provider){
34370         this.provider = provider;
34371         var state = provider.get(layout.id+"-layout-state");
34372         if(state){
34373             var wasUpdating = layout.isUpdating();
34374             if(!wasUpdating){
34375                 layout.beginUpdate();
34376             }
34377             for(var key in state){
34378                 if(typeof state[key] != "function"){
34379                     var rstate = state[key];
34380                     var r = layout.getRegion(key);
34381                     if(r && rstate){
34382                         if(rstate.size){
34383                             r.resizeTo(rstate.size);
34384                         }
34385                         if(rstate.collapsed == true){
34386                             r.collapse(true);
34387                         }else{
34388                             r.expand(null, true);
34389                         }
34390                     }
34391                 }
34392             }
34393             if(!wasUpdating){
34394                 layout.endUpdate();
34395             }
34396             this.state = state; 
34397         }
34398         this.layout = layout;
34399         layout.on("regionresized", this.onRegionResized, this);
34400         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34401         layout.on("regionexpanded", this.onRegionExpanded, this);
34402     },
34403     
34404     storeState : function(){
34405         this.provider.set(this.layout.id+"-layout-state", this.state);
34406     },
34407     
34408     onRegionResized : function(region, newSize){
34409         this.state[region.getPosition()].size = newSize;
34410         this.storeState();
34411     },
34412     
34413     onRegionCollapsed : function(region){
34414         this.state[region.getPosition()].collapsed = true;
34415         this.storeState();
34416     },
34417     
34418     onRegionExpanded : function(region){
34419         this.state[region.getPosition()].collapsed = false;
34420         this.storeState();
34421     }
34422 };/*
34423  * Based on:
34424  * Ext JS Library 1.1.1
34425  * Copyright(c) 2006-2007, Ext JS, LLC.
34426  *
34427  * Originally Released Under LGPL - original licence link has changed is not relivant.
34428  *
34429  * Fork - LGPL
34430  * <script type="text/javascript">
34431  */
34432 /**
34433  * @class Roo.ContentPanel
34434  * @extends Roo.util.Observable
34435  * A basic ContentPanel element.
34436  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34437  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34438  * @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
34439  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34440  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34441  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34442  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34443  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34444  * @cfg {String} title          The title for this panel
34445  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34446  * @cfg {String} url            Calls {@link #setUrl} with this value
34447  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34448  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34449  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34450  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34451
34452  * @constructor
34453  * Create a new ContentPanel.
34454  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34455  * @param {String/Object} config A string to set only the title or a config object
34456  * @param {String} content (optional) Set the HTML content for this panel
34457  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34458  */
34459 Roo.ContentPanel = function(el, config, content){
34460     
34461      
34462     /*
34463     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34464         config = el;
34465         el = Roo.id();
34466     }
34467     if (config && config.parentLayout) { 
34468         el = config.parentLayout.el.createChild(); 
34469     }
34470     */
34471     if(el.autoCreate){ // xtype is available if this is called from factory
34472         config = el;
34473         el = Roo.id();
34474     }
34475     this.el = Roo.get(el);
34476     if(!this.el && config && config.autoCreate){
34477         if(typeof config.autoCreate == "object"){
34478             if(!config.autoCreate.id){
34479                 config.autoCreate.id = config.id||el;
34480             }
34481             this.el = Roo.DomHelper.append(document.body,
34482                         config.autoCreate, true);
34483         }else{
34484             this.el = Roo.DomHelper.append(document.body,
34485                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34486         }
34487     }
34488     this.closable = false;
34489     this.loaded = false;
34490     this.active = false;
34491     if(typeof config == "string"){
34492         this.title = config;
34493     }else{
34494         Roo.apply(this, config);
34495     }
34496     
34497     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34498         this.wrapEl = this.el.wrap();
34499         this.toolbar.container = this.el.insertSibling(false, 'before');
34500         this.toolbar = new Roo.Toolbar(this.toolbar);
34501     }
34502     
34503     // xtype created footer. - not sure if will work as we normally have to render first..
34504     if (this.footer && !this.footer.el && this.footer.xtype) {
34505         if (!this.wrapEl) {
34506             this.wrapEl = this.el.wrap();
34507         }
34508     
34509         this.footer.container = this.wrapEl.createChild();
34510          
34511         this.footer = Roo.factory(this.footer, Roo);
34512         
34513     }
34514     
34515     if(this.resizeEl){
34516         this.resizeEl = Roo.get(this.resizeEl, true);
34517     }else{
34518         this.resizeEl = this.el;
34519     }
34520     // handle view.xtype
34521     
34522  
34523     
34524     
34525     this.addEvents({
34526         /**
34527          * @event activate
34528          * Fires when this panel is activated. 
34529          * @param {Roo.ContentPanel} this
34530          */
34531         "activate" : true,
34532         /**
34533          * @event deactivate
34534          * Fires when this panel is activated. 
34535          * @param {Roo.ContentPanel} this
34536          */
34537         "deactivate" : true,
34538
34539         /**
34540          * @event resize
34541          * Fires when this panel is resized if fitToFrame is true.
34542          * @param {Roo.ContentPanel} this
34543          * @param {Number} width The width after any component adjustments
34544          * @param {Number} height The height after any component adjustments
34545          */
34546         "resize" : true,
34547         
34548          /**
34549          * @event render
34550          * Fires when this tab is created
34551          * @param {Roo.ContentPanel} this
34552          */
34553         "render" : true
34554         
34555         
34556         
34557     });
34558     
34559
34560     
34561     
34562     if(this.autoScroll){
34563         this.resizeEl.setStyle("overflow", "auto");
34564     } else {
34565         // fix randome scrolling
34566         this.el.on('scroll', function() {
34567             Roo.log('fix random scolling');
34568             this.scrollTo('top',0); 
34569         });
34570     }
34571     content = content || this.content;
34572     if(content){
34573         this.setContent(content);
34574     }
34575     if(config && config.url){
34576         this.setUrl(this.url, this.params, this.loadOnce);
34577     }
34578     
34579     
34580     
34581     Roo.ContentPanel.superclass.constructor.call(this);
34582     
34583     if (this.view && typeof(this.view.xtype) != 'undefined') {
34584         this.view.el = this.el.appendChild(document.createElement("div"));
34585         this.view = Roo.factory(this.view); 
34586         this.view.render  &&  this.view.render(false, '');  
34587     }
34588     
34589     
34590     this.fireEvent('render', this);
34591 };
34592
34593 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34594     tabTip:'',
34595     setRegion : function(region){
34596         this.region = region;
34597         if(region){
34598            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34599         }else{
34600            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34601         } 
34602     },
34603     
34604     /**
34605      * Returns the toolbar for this Panel if one was configured. 
34606      * @return {Roo.Toolbar} 
34607      */
34608     getToolbar : function(){
34609         return this.toolbar;
34610     },
34611     
34612     setActiveState : function(active){
34613         this.active = active;
34614         if(!active){
34615             this.fireEvent("deactivate", this);
34616         }else{
34617             this.fireEvent("activate", this);
34618         }
34619     },
34620     /**
34621      * Updates this panel's element
34622      * @param {String} content The new content
34623      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34624     */
34625     setContent : function(content, loadScripts){
34626         this.el.update(content, loadScripts);
34627     },
34628
34629     ignoreResize : function(w, h){
34630         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34631             return true;
34632         }else{
34633             this.lastSize = {width: w, height: h};
34634             return false;
34635         }
34636     },
34637     /**
34638      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34639      * @return {Roo.UpdateManager} The UpdateManager
34640      */
34641     getUpdateManager : function(){
34642         return this.el.getUpdateManager();
34643     },
34644      /**
34645      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34646      * @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:
34647 <pre><code>
34648 panel.load({
34649     url: "your-url.php",
34650     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34651     callback: yourFunction,
34652     scope: yourObject, //(optional scope)
34653     discardUrl: false,
34654     nocache: false,
34655     text: "Loading...",
34656     timeout: 30,
34657     scripts: false
34658 });
34659 </code></pre>
34660      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34661      * 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.
34662      * @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}
34663      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34664      * @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.
34665      * @return {Roo.ContentPanel} this
34666      */
34667     load : function(){
34668         var um = this.el.getUpdateManager();
34669         um.update.apply(um, arguments);
34670         return this;
34671     },
34672
34673
34674     /**
34675      * 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.
34676      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34677      * @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)
34678      * @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)
34679      * @return {Roo.UpdateManager} The UpdateManager
34680      */
34681     setUrl : function(url, params, loadOnce){
34682         if(this.refreshDelegate){
34683             this.removeListener("activate", this.refreshDelegate);
34684         }
34685         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34686         this.on("activate", this.refreshDelegate);
34687         return this.el.getUpdateManager();
34688     },
34689     
34690     _handleRefresh : function(url, params, loadOnce){
34691         if(!loadOnce || !this.loaded){
34692             var updater = this.el.getUpdateManager();
34693             updater.update(url, params, this._setLoaded.createDelegate(this));
34694         }
34695     },
34696     
34697     _setLoaded : function(){
34698         this.loaded = true;
34699     }, 
34700     
34701     /**
34702      * Returns this panel's id
34703      * @return {String} 
34704      */
34705     getId : function(){
34706         return this.el.id;
34707     },
34708     
34709     /** 
34710      * Returns this panel's element - used by regiosn to add.
34711      * @return {Roo.Element} 
34712      */
34713     getEl : function(){
34714         return this.wrapEl || this.el;
34715     },
34716     
34717     adjustForComponents : function(width, height)
34718     {
34719         //Roo.log('adjustForComponents ');
34720         if(this.resizeEl != this.el){
34721             width -= this.el.getFrameWidth('lr');
34722             height -= this.el.getFrameWidth('tb');
34723         }
34724         if(this.toolbar){
34725             var te = this.toolbar.getEl();
34726             height -= te.getHeight();
34727             te.setWidth(width);
34728         }
34729         if(this.footer){
34730             var te = this.footer.getEl();
34731             Roo.log("footer:" + te.getHeight());
34732             
34733             height -= te.getHeight();
34734             te.setWidth(width);
34735         }
34736         
34737         
34738         if(this.adjustments){
34739             width += this.adjustments[0];
34740             height += this.adjustments[1];
34741         }
34742         return {"width": width, "height": height};
34743     },
34744     
34745     setSize : function(width, height){
34746         if(this.fitToFrame && !this.ignoreResize(width, height)){
34747             if(this.fitContainer && this.resizeEl != this.el){
34748                 this.el.setSize(width, height);
34749             }
34750             var size = this.adjustForComponents(width, height);
34751             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34752             this.fireEvent('resize', this, size.width, size.height);
34753         }
34754     },
34755     
34756     /**
34757      * Returns this panel's title
34758      * @return {String} 
34759      */
34760     getTitle : function(){
34761         return this.title;
34762     },
34763     
34764     /**
34765      * Set this panel's title
34766      * @param {String} title
34767      */
34768     setTitle : function(title){
34769         this.title = title;
34770         if(this.region){
34771             this.region.updatePanelTitle(this, title);
34772         }
34773     },
34774     
34775     /**
34776      * Returns true is this panel was configured to be closable
34777      * @return {Boolean} 
34778      */
34779     isClosable : function(){
34780         return this.closable;
34781     },
34782     
34783     beforeSlide : function(){
34784         this.el.clip();
34785         this.resizeEl.clip();
34786     },
34787     
34788     afterSlide : function(){
34789         this.el.unclip();
34790         this.resizeEl.unclip();
34791     },
34792     
34793     /**
34794      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34795      *   Will fail silently if the {@link #setUrl} method has not been called.
34796      *   This does not activate the panel, just updates its content.
34797      */
34798     refresh : function(){
34799         if(this.refreshDelegate){
34800            this.loaded = false;
34801            this.refreshDelegate();
34802         }
34803     },
34804     
34805     /**
34806      * Destroys this panel
34807      */
34808     destroy : function(){
34809         this.el.removeAllListeners();
34810         var tempEl = document.createElement("span");
34811         tempEl.appendChild(this.el.dom);
34812         tempEl.innerHTML = "";
34813         this.el.remove();
34814         this.el = null;
34815     },
34816     
34817     /**
34818      * form - if the content panel contains a form - this is a reference to it.
34819      * @type {Roo.form.Form}
34820      */
34821     form : false,
34822     /**
34823      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34824      *    This contains a reference to it.
34825      * @type {Roo.View}
34826      */
34827     view : false,
34828     
34829       /**
34830      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34831      * <pre><code>
34832
34833 layout.addxtype({
34834        xtype : 'Form',
34835        items: [ .... ]
34836    }
34837 );
34838
34839 </code></pre>
34840      * @param {Object} cfg Xtype definition of item to add.
34841      */
34842     
34843     addxtype : function(cfg) {
34844         // add form..
34845         if (cfg.xtype.match(/^Form$/)) {
34846             
34847             var el;
34848             //if (this.footer) {
34849             //    el = this.footer.container.insertSibling(false, 'before');
34850             //} else {
34851                 el = this.el.createChild();
34852             //}
34853
34854             this.form = new  Roo.form.Form(cfg);
34855             
34856             
34857             if ( this.form.allItems.length) this.form.render(el.dom);
34858             return this.form;
34859         }
34860         // should only have one of theses..
34861         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34862             // views.. should not be just added - used named prop 'view''
34863             
34864             cfg.el = this.el.appendChild(document.createElement("div"));
34865             // factory?
34866             
34867             var ret = new Roo.factory(cfg);
34868              
34869              ret.render && ret.render(false, ''); // render blank..
34870             this.view = ret;
34871             return ret;
34872         }
34873         return false;
34874     }
34875 });
34876
34877 /**
34878  * @class Roo.GridPanel
34879  * @extends Roo.ContentPanel
34880  * @constructor
34881  * Create a new GridPanel.
34882  * @param {Roo.grid.Grid} grid The grid for this panel
34883  * @param {String/Object} config A string to set only the panel's title, or a config object
34884  */
34885 Roo.GridPanel = function(grid, config){
34886     
34887   
34888     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34889         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34890         
34891     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34892     
34893     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34894     
34895     if(this.toolbar){
34896         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34897     }
34898     // xtype created footer. - not sure if will work as we normally have to render first..
34899     if (this.footer && !this.footer.el && this.footer.xtype) {
34900         
34901         this.footer.container = this.grid.getView().getFooterPanel(true);
34902         this.footer.dataSource = this.grid.dataSource;
34903         this.footer = Roo.factory(this.footer, Roo);
34904         
34905     }
34906     
34907     grid.monitorWindowResize = false; // turn off autosizing
34908     grid.autoHeight = false;
34909     grid.autoWidth = false;
34910     this.grid = grid;
34911     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34912 };
34913
34914 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34915     getId : function(){
34916         return this.grid.id;
34917     },
34918     
34919     /**
34920      * Returns the grid for this panel
34921      * @return {Roo.grid.Grid} 
34922      */
34923     getGrid : function(){
34924         return this.grid;    
34925     },
34926     
34927     setSize : function(width, height){
34928         if(!this.ignoreResize(width, height)){
34929             var grid = this.grid;
34930             var size = this.adjustForComponents(width, height);
34931             grid.getGridEl().setSize(size.width, size.height);
34932             grid.autoSize();
34933         }
34934     },
34935     
34936     beforeSlide : function(){
34937         this.grid.getView().scroller.clip();
34938     },
34939     
34940     afterSlide : function(){
34941         this.grid.getView().scroller.unclip();
34942     },
34943     
34944     destroy : function(){
34945         this.grid.destroy();
34946         delete this.grid;
34947         Roo.GridPanel.superclass.destroy.call(this); 
34948     }
34949 });
34950
34951
34952 /**
34953  * @class Roo.NestedLayoutPanel
34954  * @extends Roo.ContentPanel
34955  * @constructor
34956  * Create a new NestedLayoutPanel.
34957  * 
34958  * 
34959  * @param {Roo.BorderLayout} layout The layout for this panel
34960  * @param {String/Object} config A string to set only the title or a config object
34961  */
34962 Roo.NestedLayoutPanel = function(layout, config)
34963 {
34964     // construct with only one argument..
34965     /* FIXME - implement nicer consturctors
34966     if (layout.layout) {
34967         config = layout;
34968         layout = config.layout;
34969         delete config.layout;
34970     }
34971     if (layout.xtype && !layout.getEl) {
34972         // then layout needs constructing..
34973         layout = Roo.factory(layout, Roo);
34974     }
34975     */
34976     
34977     
34978     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
34979     
34980     layout.monitorWindowResize = false; // turn off autosizing
34981     this.layout = layout;
34982     this.layout.getEl().addClass("x-layout-nested-layout");
34983     
34984     
34985     
34986     
34987 };
34988
34989 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
34990
34991     setSize : function(width, height){
34992         if(!this.ignoreResize(width, height)){
34993             var size = this.adjustForComponents(width, height);
34994             var el = this.layout.getEl();
34995             el.setSize(size.width, size.height);
34996             var touch = el.dom.offsetWidth;
34997             this.layout.layout();
34998             // ie requires a double layout on the first pass
34999             if(Roo.isIE && !this.initialized){
35000                 this.initialized = true;
35001                 this.layout.layout();
35002             }
35003         }
35004     },
35005     
35006     // activate all subpanels if not currently active..
35007     
35008     setActiveState : function(active){
35009         this.active = active;
35010         if(!active){
35011             this.fireEvent("deactivate", this);
35012             return;
35013         }
35014         
35015         this.fireEvent("activate", this);
35016         // not sure if this should happen before or after..
35017         if (!this.layout) {
35018             return; // should not happen..
35019         }
35020         var reg = false;
35021         for (var r in this.layout.regions) {
35022             reg = this.layout.getRegion(r);
35023             if (reg.getActivePanel()) {
35024                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35025                 reg.setActivePanel(reg.getActivePanel());
35026                 continue;
35027             }
35028             if (!reg.panels.length) {
35029                 continue;
35030             }
35031             reg.showPanel(reg.getPanel(0));
35032         }
35033         
35034         
35035         
35036         
35037     },
35038     
35039     /**
35040      * Returns the nested BorderLayout for this panel
35041      * @return {Roo.BorderLayout} 
35042      */
35043     getLayout : function(){
35044         return this.layout;
35045     },
35046     
35047      /**
35048      * Adds a xtype elements to the layout of the nested panel
35049      * <pre><code>
35050
35051 panel.addxtype({
35052        xtype : 'ContentPanel',
35053        region: 'west',
35054        items: [ .... ]
35055    }
35056 );
35057
35058 panel.addxtype({
35059         xtype : 'NestedLayoutPanel',
35060         region: 'west',
35061         layout: {
35062            center: { },
35063            west: { }   
35064         },
35065         items : [ ... list of content panels or nested layout panels.. ]
35066    }
35067 );
35068 </code></pre>
35069      * @param {Object} cfg Xtype definition of item to add.
35070      */
35071     addxtype : function(cfg) {
35072         return this.layout.addxtype(cfg);
35073     
35074     }
35075 });
35076
35077 Roo.ScrollPanel = function(el, config, content){
35078     config = config || {};
35079     config.fitToFrame = true;
35080     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35081     
35082     this.el.dom.style.overflow = "hidden";
35083     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35084     this.el.removeClass("x-layout-inactive-content");
35085     this.el.on("mousewheel", this.onWheel, this);
35086
35087     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35088     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35089     up.unselectable(); down.unselectable();
35090     up.on("click", this.scrollUp, this);
35091     down.on("click", this.scrollDown, this);
35092     up.addClassOnOver("x-scroller-btn-over");
35093     down.addClassOnOver("x-scroller-btn-over");
35094     up.addClassOnClick("x-scroller-btn-click");
35095     down.addClassOnClick("x-scroller-btn-click");
35096     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35097
35098     this.resizeEl = this.el;
35099     this.el = wrap; this.up = up; this.down = down;
35100 };
35101
35102 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35103     increment : 100,
35104     wheelIncrement : 5,
35105     scrollUp : function(){
35106         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35107     },
35108
35109     scrollDown : function(){
35110         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35111     },
35112
35113     afterScroll : function(){
35114         var el = this.resizeEl;
35115         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35116         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35117         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35118     },
35119
35120     setSize : function(){
35121         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35122         this.afterScroll();
35123     },
35124
35125     onWheel : function(e){
35126         var d = e.getWheelDelta();
35127         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35128         this.afterScroll();
35129         e.stopEvent();
35130     },
35131
35132     setContent : function(content, loadScripts){
35133         this.resizeEl.update(content, loadScripts);
35134     }
35135
35136 });
35137
35138
35139
35140
35141
35142
35143
35144
35145
35146 /**
35147  * @class Roo.TreePanel
35148  * @extends Roo.ContentPanel
35149  * @constructor
35150  * Create a new TreePanel. - defaults to fit/scoll contents.
35151  * @param {String/Object} config A string to set only the panel's title, or a config object
35152  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35153  */
35154 Roo.TreePanel = function(config){
35155     var el = config.el;
35156     var tree = config.tree;
35157     delete config.tree; 
35158     delete config.el; // hopefull!
35159     
35160     // wrapper for IE7 strict & safari scroll issue
35161     
35162     var treeEl = el.createChild();
35163     config.resizeEl = treeEl;
35164     
35165     
35166     
35167     Roo.TreePanel.superclass.constructor.call(this, el, config);
35168  
35169  
35170     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35171     //console.log(tree);
35172     this.on('activate', function()
35173     {
35174         if (this.tree.rendered) {
35175             return;
35176         }
35177         //console.log('render tree');
35178         this.tree.render();
35179     });
35180     // this should not be needed.. - it's actually the 'el' that resizes?
35181     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35182     
35183     //this.on('resize',  function (cp, w, h) {
35184     //        this.tree.innerCt.setWidth(w);
35185     //        this.tree.innerCt.setHeight(h);
35186     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35187     //});
35188
35189         
35190     
35191 };
35192
35193 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35194     fitToFrame : true,
35195     autoScroll : true
35196 });
35197
35198
35199
35200
35201
35202
35203
35204
35205
35206
35207
35208 /*
35209  * Based on:
35210  * Ext JS Library 1.1.1
35211  * Copyright(c) 2006-2007, Ext JS, LLC.
35212  *
35213  * Originally Released Under LGPL - original licence link has changed is not relivant.
35214  *
35215  * Fork - LGPL
35216  * <script type="text/javascript">
35217  */
35218  
35219
35220 /**
35221  * @class Roo.ReaderLayout
35222  * @extends Roo.BorderLayout
35223  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35224  * center region containing two nested regions (a top one for a list view and one for item preview below),
35225  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35226  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35227  * expedites the setup of the overall layout and regions for this common application style.
35228  * Example:
35229  <pre><code>
35230 var reader = new Roo.ReaderLayout();
35231 var CP = Roo.ContentPanel;  // shortcut for adding
35232
35233 reader.beginUpdate();
35234 reader.add("north", new CP("north", "North"));
35235 reader.add("west", new CP("west", {title: "West"}));
35236 reader.add("east", new CP("east", {title: "East"}));
35237
35238 reader.regions.listView.add(new CP("listView", "List"));
35239 reader.regions.preview.add(new CP("preview", "Preview"));
35240 reader.endUpdate();
35241 </code></pre>
35242 * @constructor
35243 * Create a new ReaderLayout
35244 * @param {Object} config Configuration options
35245 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35246 * document.body if omitted)
35247 */
35248 Roo.ReaderLayout = function(config, renderTo){
35249     var c = config || {size:{}};
35250     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35251         north: c.north !== false ? Roo.apply({
35252             split:false,
35253             initialSize: 32,
35254             titlebar: false
35255         }, c.north) : false,
35256         west: c.west !== false ? Roo.apply({
35257             split:true,
35258             initialSize: 200,
35259             minSize: 175,
35260             maxSize: 400,
35261             titlebar: true,
35262             collapsible: true,
35263             animate: true,
35264             margins:{left:5,right:0,bottom:5,top:5},
35265             cmargins:{left:5,right:5,bottom:5,top:5}
35266         }, c.west) : false,
35267         east: c.east !== false ? Roo.apply({
35268             split:true,
35269             initialSize: 200,
35270             minSize: 175,
35271             maxSize: 400,
35272             titlebar: true,
35273             collapsible: true,
35274             animate: true,
35275             margins:{left:0,right:5,bottom:5,top:5},
35276             cmargins:{left:5,right:5,bottom:5,top:5}
35277         }, c.east) : false,
35278         center: Roo.apply({
35279             tabPosition: 'top',
35280             autoScroll:false,
35281             closeOnTab: true,
35282             titlebar:false,
35283             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35284         }, c.center)
35285     });
35286
35287     this.el.addClass('x-reader');
35288
35289     this.beginUpdate();
35290
35291     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35292         south: c.preview !== false ? Roo.apply({
35293             split:true,
35294             initialSize: 200,
35295             minSize: 100,
35296             autoScroll:true,
35297             collapsible:true,
35298             titlebar: true,
35299             cmargins:{top:5,left:0, right:0, bottom:0}
35300         }, c.preview) : false,
35301         center: Roo.apply({
35302             autoScroll:false,
35303             titlebar:false,
35304             minHeight:200
35305         }, c.listView)
35306     });
35307     this.add('center', new Roo.NestedLayoutPanel(inner,
35308             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35309
35310     this.endUpdate();
35311
35312     this.regions.preview = inner.getRegion('south');
35313     this.regions.listView = inner.getRegion('center');
35314 };
35315
35316 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35317  * Based on:
35318  * Ext JS Library 1.1.1
35319  * Copyright(c) 2006-2007, Ext JS, LLC.
35320  *
35321  * Originally Released Under LGPL - original licence link has changed is not relivant.
35322  *
35323  * Fork - LGPL
35324  * <script type="text/javascript">
35325  */
35326  
35327 /**
35328  * @class Roo.grid.Grid
35329  * @extends Roo.util.Observable
35330  * This class represents the primary interface of a component based grid control.
35331  * <br><br>Usage:<pre><code>
35332  var grid = new Roo.grid.Grid("my-container-id", {
35333      ds: myDataStore,
35334      cm: myColModel,
35335      selModel: mySelectionModel,
35336      autoSizeColumns: true,
35337      monitorWindowResize: false,
35338      trackMouseOver: true
35339  });
35340  // set any options
35341  grid.render();
35342  * </code></pre>
35343  * <b>Common Problems:</b><br/>
35344  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35345  * element will correct this<br/>
35346  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35347  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35348  * are unpredictable.<br/>
35349  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35350  * grid to calculate dimensions/offsets.<br/>
35351   * @constructor
35352  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35353  * The container MUST have some type of size defined for the grid to fill. The container will be
35354  * automatically set to position relative if it isn't already.
35355  * @param {Object} config A config object that sets properties on this grid.
35356  */
35357 Roo.grid.Grid = function(container, config){
35358         // initialize the container
35359         this.container = Roo.get(container);
35360         this.container.update("");
35361         this.container.setStyle("overflow", "hidden");
35362     this.container.addClass('x-grid-container');
35363
35364     this.id = this.container.id;
35365
35366     Roo.apply(this, config);
35367     // check and correct shorthanded configs
35368     if(this.ds){
35369         this.dataSource = this.ds;
35370         delete this.ds;
35371     }
35372     if(this.cm){
35373         this.colModel = this.cm;
35374         delete this.cm;
35375     }
35376     if(this.sm){
35377         this.selModel = this.sm;
35378         delete this.sm;
35379     }
35380
35381     if (this.selModel) {
35382         this.selModel = Roo.factory(this.selModel, Roo.grid);
35383         this.sm = this.selModel;
35384         this.sm.xmodule = this.xmodule || false;
35385     }
35386     if (typeof(this.colModel.config) == 'undefined') {
35387         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35388         this.cm = this.colModel;
35389         this.cm.xmodule = this.xmodule || false;
35390     }
35391     if (this.dataSource) {
35392         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35393         this.ds = this.dataSource;
35394         this.ds.xmodule = this.xmodule || false;
35395          
35396     }
35397     
35398     
35399     
35400     if(this.width){
35401         this.container.setWidth(this.width);
35402     }
35403
35404     if(this.height){
35405         this.container.setHeight(this.height);
35406     }
35407     /** @private */
35408         this.addEvents({
35409         // raw events
35410         /**
35411          * @event click
35412          * The raw click event for the entire grid.
35413          * @param {Roo.EventObject} e
35414          */
35415         "click" : true,
35416         /**
35417          * @event dblclick
35418          * The raw dblclick event for the entire grid.
35419          * @param {Roo.EventObject} e
35420          */
35421         "dblclick" : true,
35422         /**
35423          * @event contextmenu
35424          * The raw contextmenu event for the entire grid.
35425          * @param {Roo.EventObject} e
35426          */
35427         "contextmenu" : true,
35428         /**
35429          * @event mousedown
35430          * The raw mousedown event for the entire grid.
35431          * @param {Roo.EventObject} e
35432          */
35433         "mousedown" : true,
35434         /**
35435          * @event mouseup
35436          * The raw mouseup event for the entire grid.
35437          * @param {Roo.EventObject} e
35438          */
35439         "mouseup" : true,
35440         /**
35441          * @event mouseover
35442          * The raw mouseover event for the entire grid.
35443          * @param {Roo.EventObject} e
35444          */
35445         "mouseover" : true,
35446         /**
35447          * @event mouseout
35448          * The raw mouseout event for the entire grid.
35449          * @param {Roo.EventObject} e
35450          */
35451         "mouseout" : true,
35452         /**
35453          * @event keypress
35454          * The raw keypress event for the entire grid.
35455          * @param {Roo.EventObject} e
35456          */
35457         "keypress" : true,
35458         /**
35459          * @event keydown
35460          * The raw keydown event for the entire grid.
35461          * @param {Roo.EventObject} e
35462          */
35463         "keydown" : true,
35464
35465         // custom events
35466
35467         /**
35468          * @event cellclick
35469          * Fires when a cell is clicked
35470          * @param {Grid} this
35471          * @param {Number} rowIndex
35472          * @param {Number} columnIndex
35473          * @param {Roo.EventObject} e
35474          */
35475         "cellclick" : true,
35476         /**
35477          * @event celldblclick
35478          * Fires when a cell is double clicked
35479          * @param {Grid} this
35480          * @param {Number} rowIndex
35481          * @param {Number} columnIndex
35482          * @param {Roo.EventObject} e
35483          */
35484         "celldblclick" : true,
35485         /**
35486          * @event rowclick
35487          * Fires when a row is clicked
35488          * @param {Grid} this
35489          * @param {Number} rowIndex
35490          * @param {Roo.EventObject} e
35491          */
35492         "rowclick" : true,
35493         /**
35494          * @event rowdblclick
35495          * Fires when a row is double clicked
35496          * @param {Grid} this
35497          * @param {Number} rowIndex
35498          * @param {Roo.EventObject} e
35499          */
35500         "rowdblclick" : true,
35501         /**
35502          * @event headerclick
35503          * Fires when a header is clicked
35504          * @param {Grid} this
35505          * @param {Number} columnIndex
35506          * @param {Roo.EventObject} e
35507          */
35508         "headerclick" : true,
35509         /**
35510          * @event headerdblclick
35511          * Fires when a header cell is double clicked
35512          * @param {Grid} this
35513          * @param {Number} columnIndex
35514          * @param {Roo.EventObject} e
35515          */
35516         "headerdblclick" : true,
35517         /**
35518          * @event rowcontextmenu
35519          * Fires when a row is right clicked
35520          * @param {Grid} this
35521          * @param {Number} rowIndex
35522          * @param {Roo.EventObject} e
35523          */
35524         "rowcontextmenu" : true,
35525         /**
35526          * @event cellcontextmenu
35527          * Fires when a cell is right clicked
35528          * @param {Grid} this
35529          * @param {Number} rowIndex
35530          * @param {Number} cellIndex
35531          * @param {Roo.EventObject} e
35532          */
35533          "cellcontextmenu" : true,
35534         /**
35535          * @event headercontextmenu
35536          * Fires when a header is right clicked
35537          * @param {Grid} this
35538          * @param {Number} columnIndex
35539          * @param {Roo.EventObject} e
35540          */
35541         "headercontextmenu" : true,
35542         /**
35543          * @event bodyscroll
35544          * Fires when the body element is scrolled
35545          * @param {Number} scrollLeft
35546          * @param {Number} scrollTop
35547          */
35548         "bodyscroll" : true,
35549         /**
35550          * @event columnresize
35551          * Fires when the user resizes a column
35552          * @param {Number} columnIndex
35553          * @param {Number} newSize
35554          */
35555         "columnresize" : true,
35556         /**
35557          * @event columnmove
35558          * Fires when the user moves a column
35559          * @param {Number} oldIndex
35560          * @param {Number} newIndex
35561          */
35562         "columnmove" : true,
35563         /**
35564          * @event startdrag
35565          * Fires when row(s) start being dragged
35566          * @param {Grid} this
35567          * @param {Roo.GridDD} dd The drag drop object
35568          * @param {event} e The raw browser event
35569          */
35570         "startdrag" : true,
35571         /**
35572          * @event enddrag
35573          * Fires when a drag operation is complete
35574          * @param {Grid} this
35575          * @param {Roo.GridDD} dd The drag drop object
35576          * @param {event} e The raw browser event
35577          */
35578         "enddrag" : true,
35579         /**
35580          * @event dragdrop
35581          * Fires when dragged row(s) are dropped on a valid DD target
35582          * @param {Grid} this
35583          * @param {Roo.GridDD} dd The drag drop object
35584          * @param {String} targetId The target drag drop object
35585          * @param {event} e The raw browser event
35586          */
35587         "dragdrop" : true,
35588         /**
35589          * @event dragover
35590          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35591          * @param {Grid} this
35592          * @param {Roo.GridDD} dd The drag drop object
35593          * @param {String} targetId The target drag drop object
35594          * @param {event} e The raw browser event
35595          */
35596         "dragover" : true,
35597         /**
35598          * @event dragenter
35599          *  Fires when the dragged row(s) first cross another DD target while being dragged
35600          * @param {Grid} this
35601          * @param {Roo.GridDD} dd The drag drop object
35602          * @param {String} targetId The target drag drop object
35603          * @param {event} e The raw browser event
35604          */
35605         "dragenter" : true,
35606         /**
35607          * @event dragout
35608          * Fires when the dragged row(s) leave another DD target while being dragged
35609          * @param {Grid} this
35610          * @param {Roo.GridDD} dd The drag drop object
35611          * @param {String} targetId The target drag drop object
35612          * @param {event} e The raw browser event
35613          */
35614         "dragout" : true,
35615         /**
35616          * @event rowclass
35617          * Fires when a row is rendered, so you can change add a style to it.
35618          * @param {GridView} gridview   The grid view
35619          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35620          */
35621         'rowclass' : true,
35622
35623         /**
35624          * @event render
35625          * Fires when the grid is rendered
35626          * @param {Grid} grid
35627          */
35628         'render' : true
35629     });
35630
35631     Roo.grid.Grid.superclass.constructor.call(this);
35632 };
35633 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35634     
35635     /**
35636      * @cfg {String} ddGroup - drag drop group.
35637      */
35638
35639     /**
35640      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35641      */
35642     minColumnWidth : 25,
35643
35644     /**
35645      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35646      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35647      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35648      */
35649     autoSizeColumns : false,
35650
35651     /**
35652      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35653      */
35654     autoSizeHeaders : true,
35655
35656     /**
35657      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35658      */
35659     monitorWindowResize : true,
35660
35661     /**
35662      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35663      * rows measured to get a columns size. Default is 0 (all rows).
35664      */
35665     maxRowsToMeasure : 0,
35666
35667     /**
35668      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35669      */
35670     trackMouseOver : true,
35671
35672     /**
35673     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35674     */
35675     
35676     /**
35677     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35678     */
35679     enableDragDrop : false,
35680     
35681     /**
35682     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35683     */
35684     enableColumnMove : true,
35685     
35686     /**
35687     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35688     */
35689     enableColumnHide : true,
35690     
35691     /**
35692     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35693     */
35694     enableRowHeightSync : false,
35695     
35696     /**
35697     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35698     */
35699     stripeRows : true,
35700     
35701     /**
35702     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35703     */
35704     autoHeight : false,
35705
35706     /**
35707      * @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.
35708      */
35709     autoExpandColumn : false,
35710
35711     /**
35712     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35713     * Default is 50.
35714     */
35715     autoExpandMin : 50,
35716
35717     /**
35718     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35719     */
35720     autoExpandMax : 1000,
35721
35722     /**
35723     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35724     */
35725     view : null,
35726
35727     /**
35728     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35729     */
35730     loadMask : false,
35731     /**
35732     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35733     */
35734     dropTarget: false,
35735     
35736    
35737     
35738     // private
35739     rendered : false,
35740
35741     /**
35742     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35743     * of a fixed width. Default is false.
35744     */
35745     /**
35746     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35747     */
35748     /**
35749      * Called once after all setup has been completed and the grid is ready to be rendered.
35750      * @return {Roo.grid.Grid} this
35751      */
35752     render : function()
35753     {
35754         var c = this.container;
35755         // try to detect autoHeight/width mode
35756         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35757             this.autoHeight = true;
35758         }
35759         var view = this.getView();
35760         view.init(this);
35761
35762         c.on("click", this.onClick, this);
35763         c.on("dblclick", this.onDblClick, this);
35764         c.on("contextmenu", this.onContextMenu, this);
35765         c.on("keydown", this.onKeyDown, this);
35766         if (Roo.isTouch) {
35767             c.on("touchstart", this.onTouchStart, this);
35768         }
35769
35770         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35771
35772         this.getSelectionModel().init(this);
35773
35774         view.render();
35775
35776         if(this.loadMask){
35777             this.loadMask = new Roo.LoadMask(this.container,
35778                     Roo.apply({store:this.dataSource}, this.loadMask));
35779         }
35780         
35781         
35782         if (this.toolbar && this.toolbar.xtype) {
35783             this.toolbar.container = this.getView().getHeaderPanel(true);
35784             this.toolbar = new Roo.Toolbar(this.toolbar);
35785         }
35786         if (this.footer && this.footer.xtype) {
35787             this.footer.dataSource = this.getDataSource();
35788             this.footer.container = this.getView().getFooterPanel(true);
35789             this.footer = Roo.factory(this.footer, Roo);
35790         }
35791         if (this.dropTarget && this.dropTarget.xtype) {
35792             delete this.dropTarget.xtype;
35793             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35794         }
35795         
35796         
35797         this.rendered = true;
35798         this.fireEvent('render', this);
35799         return this;
35800     },
35801
35802         /**
35803          * Reconfigures the grid to use a different Store and Column Model.
35804          * The View will be bound to the new objects and refreshed.
35805          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35806          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35807          */
35808     reconfigure : function(dataSource, colModel){
35809         if(this.loadMask){
35810             this.loadMask.destroy();
35811             this.loadMask = new Roo.LoadMask(this.container,
35812                     Roo.apply({store:dataSource}, this.loadMask));
35813         }
35814         this.view.bind(dataSource, colModel);
35815         this.dataSource = dataSource;
35816         this.colModel = colModel;
35817         this.view.refresh(true);
35818     },
35819
35820     // private
35821     onKeyDown : function(e){
35822         this.fireEvent("keydown", e);
35823     },
35824
35825     /**
35826      * Destroy this grid.
35827      * @param {Boolean} removeEl True to remove the element
35828      */
35829     destroy : function(removeEl, keepListeners){
35830         if(this.loadMask){
35831             this.loadMask.destroy();
35832         }
35833         var c = this.container;
35834         c.removeAllListeners();
35835         this.view.destroy();
35836         this.colModel.purgeListeners();
35837         if(!keepListeners){
35838             this.purgeListeners();
35839         }
35840         c.update("");
35841         if(removeEl === true){
35842             c.remove();
35843         }
35844     },
35845
35846     // private
35847     processEvent : function(name, e){
35848         // does this fire select???
35849         Roo.log('grid:processEvent '  + name);
35850         
35851         if (name != 'touchstart' ) {
35852             this.fireEvent(name, e);    
35853         }
35854         
35855         var t = e.getTarget();
35856         var v = this.view;
35857         var header = v.findHeaderIndex(t);
35858         if(header !== false){
35859             var ename = name == 'touchstart' ? 'click' : name;
35860              
35861             this.fireEvent("header" + ename, this, header, e);
35862         }else{
35863             var row = v.findRowIndex(t);
35864             var cell = v.findCellIndex(t);
35865             if (name == 'touchstart') {
35866                 // first touch is always a click.
35867                 // hopefull this happens after selection is updated.?
35868                 name = false;
35869                 
35870                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35871                     var cs = this.selModel.getSelectedCell();
35872                     if (row == cs[0] && cell == cs[1]){
35873                         name = 'dblclick';
35874                     }
35875                 }
35876                 if (typeof(this.selModel.getSelections) != 'undefined') {
35877                     var cs = this.selModel.getSelections();
35878                     var ds = this.dataSource;
35879                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
35880                         name = 'dblclick';
35881                     }
35882                 }
35883                 if (!name) {
35884                     return;
35885                 }
35886             }
35887             
35888             
35889             if(row !== false){
35890                 this.fireEvent("row" + name, this, row, e);
35891                 if(cell !== false){
35892                     this.fireEvent("cell" + name, this, row, cell, e);
35893                 }
35894             }
35895         }
35896     },
35897
35898     // private
35899     onClick : function(e){
35900         this.processEvent("click", e);
35901     },
35902    // private
35903     onTouchStart : function(e){
35904         this.processEvent("touchstart", e);
35905     },
35906
35907     // private
35908     onContextMenu : function(e, t){
35909         this.processEvent("contextmenu", e);
35910     },
35911
35912     // private
35913     onDblClick : function(e){
35914         this.processEvent("dblclick", e);
35915     },
35916
35917     // private
35918     walkCells : function(row, col, step, fn, scope){
35919         var cm = this.colModel, clen = cm.getColumnCount();
35920         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35921         if(step < 0){
35922             if(col < 0){
35923                 row--;
35924                 first = false;
35925             }
35926             while(row >= 0){
35927                 if(!first){
35928                     col = clen-1;
35929                 }
35930                 first = false;
35931                 while(col >= 0){
35932                     if(fn.call(scope || this, row, col, cm) === true){
35933                         return [row, col];
35934                     }
35935                     col--;
35936                 }
35937                 row--;
35938             }
35939         } else {
35940             if(col >= clen){
35941                 row++;
35942                 first = false;
35943             }
35944             while(row < rlen){
35945                 if(!first){
35946                     col = 0;
35947                 }
35948                 first = false;
35949                 while(col < clen){
35950                     if(fn.call(scope || this, row, col, cm) === true){
35951                         return [row, col];
35952                     }
35953                     col++;
35954                 }
35955                 row++;
35956             }
35957         }
35958         return null;
35959     },
35960
35961     // private
35962     getSelections : function(){
35963         return this.selModel.getSelections();
35964     },
35965
35966     /**
35967      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
35968      * but if manual update is required this method will initiate it.
35969      */
35970     autoSize : function(){
35971         if(this.rendered){
35972             this.view.layout();
35973             if(this.view.adjustForScroll){
35974                 this.view.adjustForScroll();
35975             }
35976         }
35977     },
35978
35979     /**
35980      * Returns the grid's underlying element.
35981      * @return {Element} The element
35982      */
35983     getGridEl : function(){
35984         return this.container;
35985     },
35986
35987     // private for compatibility, overridden by editor grid
35988     stopEditing : function(){},
35989
35990     /**
35991      * Returns the grid's SelectionModel.
35992      * @return {SelectionModel}
35993      */
35994     getSelectionModel : function(){
35995         if(!this.selModel){
35996             this.selModel = new Roo.grid.RowSelectionModel();
35997         }
35998         return this.selModel;
35999     },
36000
36001     /**
36002      * Returns the grid's DataSource.
36003      * @return {DataSource}
36004      */
36005     getDataSource : function(){
36006         return this.dataSource;
36007     },
36008
36009     /**
36010      * Returns the grid's ColumnModel.
36011      * @return {ColumnModel}
36012      */
36013     getColumnModel : function(){
36014         return this.colModel;
36015     },
36016
36017     /**
36018      * Returns the grid's GridView object.
36019      * @return {GridView}
36020      */
36021     getView : function(){
36022         if(!this.view){
36023             this.view = new Roo.grid.GridView(this.viewConfig);
36024         }
36025         return this.view;
36026     },
36027     /**
36028      * Called to get grid's drag proxy text, by default returns this.ddText.
36029      * @return {String}
36030      */
36031     getDragDropText : function(){
36032         var count = this.selModel.getCount();
36033         return String.format(this.ddText, count, count == 1 ? '' : 's');
36034     }
36035 });
36036 /**
36037  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36038  * %0 is replaced with the number of selected rows.
36039  * @type String
36040  */
36041 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36042  * Based on:
36043  * Ext JS Library 1.1.1
36044  * Copyright(c) 2006-2007, Ext JS, LLC.
36045  *
36046  * Originally Released Under LGPL - original licence link has changed is not relivant.
36047  *
36048  * Fork - LGPL
36049  * <script type="text/javascript">
36050  */
36051  
36052 Roo.grid.AbstractGridView = function(){
36053         this.grid = null;
36054         
36055         this.events = {
36056             "beforerowremoved" : true,
36057             "beforerowsinserted" : true,
36058             "beforerefresh" : true,
36059             "rowremoved" : true,
36060             "rowsinserted" : true,
36061             "rowupdated" : true,
36062             "refresh" : true
36063         };
36064     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36065 };
36066
36067 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36068     rowClass : "x-grid-row",
36069     cellClass : "x-grid-cell",
36070     tdClass : "x-grid-td",
36071     hdClass : "x-grid-hd",
36072     splitClass : "x-grid-hd-split",
36073     
36074     init: function(grid){
36075         this.grid = grid;
36076                 var cid = this.grid.getGridEl().id;
36077         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36078         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36079         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36080         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36081         },
36082         
36083     getColumnRenderers : function(){
36084         var renderers = [];
36085         var cm = this.grid.colModel;
36086         var colCount = cm.getColumnCount();
36087         for(var i = 0; i < colCount; i++){
36088             renderers[i] = cm.getRenderer(i);
36089         }
36090         return renderers;
36091     },
36092     
36093     getColumnIds : function(){
36094         var ids = [];
36095         var cm = this.grid.colModel;
36096         var colCount = cm.getColumnCount();
36097         for(var i = 0; i < colCount; i++){
36098             ids[i] = cm.getColumnId(i);
36099         }
36100         return ids;
36101     },
36102     
36103     getDataIndexes : function(){
36104         if(!this.indexMap){
36105             this.indexMap = this.buildIndexMap();
36106         }
36107         return this.indexMap.colToData;
36108     },
36109     
36110     getColumnIndexByDataIndex : function(dataIndex){
36111         if(!this.indexMap){
36112             this.indexMap = this.buildIndexMap();
36113         }
36114         return this.indexMap.dataToCol[dataIndex];
36115     },
36116     
36117     /**
36118      * Set a css style for a column dynamically. 
36119      * @param {Number} colIndex The index of the column
36120      * @param {String} name The css property name
36121      * @param {String} value The css value
36122      */
36123     setCSSStyle : function(colIndex, name, value){
36124         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36125         Roo.util.CSS.updateRule(selector, name, value);
36126     },
36127     
36128     generateRules : function(cm){
36129         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36130         Roo.util.CSS.removeStyleSheet(rulesId);
36131         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36132             var cid = cm.getColumnId(i);
36133             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36134                          this.tdSelector, cid, " {\n}\n",
36135                          this.hdSelector, cid, " {\n}\n",
36136                          this.splitSelector, cid, " {\n}\n");
36137         }
36138         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36139     }
36140 });/*
36141  * Based on:
36142  * Ext JS Library 1.1.1
36143  * Copyright(c) 2006-2007, Ext JS, LLC.
36144  *
36145  * Originally Released Under LGPL - original licence link has changed is not relivant.
36146  *
36147  * Fork - LGPL
36148  * <script type="text/javascript">
36149  */
36150
36151 // private
36152 // This is a support class used internally by the Grid components
36153 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36154     this.grid = grid;
36155     this.view = grid.getView();
36156     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36157     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36158     if(hd2){
36159         this.setHandleElId(Roo.id(hd));
36160         this.setOuterHandleElId(Roo.id(hd2));
36161     }
36162     this.scroll = false;
36163 };
36164 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36165     maxDragWidth: 120,
36166     getDragData : function(e){
36167         var t = Roo.lib.Event.getTarget(e);
36168         var h = this.view.findHeaderCell(t);
36169         if(h){
36170             return {ddel: h.firstChild, header:h};
36171         }
36172         return false;
36173     },
36174
36175     onInitDrag : function(e){
36176         this.view.headersDisabled = true;
36177         var clone = this.dragData.ddel.cloneNode(true);
36178         clone.id = Roo.id();
36179         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36180         this.proxy.update(clone);
36181         return true;
36182     },
36183
36184     afterValidDrop : function(){
36185         var v = this.view;
36186         setTimeout(function(){
36187             v.headersDisabled = false;
36188         }, 50);
36189     },
36190
36191     afterInvalidDrop : function(){
36192         var v = this.view;
36193         setTimeout(function(){
36194             v.headersDisabled = false;
36195         }, 50);
36196     }
36197 });
36198 /*
36199  * Based on:
36200  * Ext JS Library 1.1.1
36201  * Copyright(c) 2006-2007, Ext JS, LLC.
36202  *
36203  * Originally Released Under LGPL - original licence link has changed is not relivant.
36204  *
36205  * Fork - LGPL
36206  * <script type="text/javascript">
36207  */
36208 // private
36209 // This is a support class used internally by the Grid components
36210 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36211     this.grid = grid;
36212     this.view = grid.getView();
36213     // split the proxies so they don't interfere with mouse events
36214     this.proxyTop = Roo.DomHelper.append(document.body, {
36215         cls:"col-move-top", html:"&#160;"
36216     }, true);
36217     this.proxyBottom = Roo.DomHelper.append(document.body, {
36218         cls:"col-move-bottom", html:"&#160;"
36219     }, true);
36220     this.proxyTop.hide = this.proxyBottom.hide = function(){
36221         this.setLeftTop(-100,-100);
36222         this.setStyle("visibility", "hidden");
36223     };
36224     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36225     // temporarily disabled
36226     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36227     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36228 };
36229 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36230     proxyOffsets : [-4, -9],
36231     fly: Roo.Element.fly,
36232
36233     getTargetFromEvent : function(e){
36234         var t = Roo.lib.Event.getTarget(e);
36235         var cindex = this.view.findCellIndex(t);
36236         if(cindex !== false){
36237             return this.view.getHeaderCell(cindex);
36238         }
36239         return null;
36240     },
36241
36242     nextVisible : function(h){
36243         var v = this.view, cm = this.grid.colModel;
36244         h = h.nextSibling;
36245         while(h){
36246             if(!cm.isHidden(v.getCellIndex(h))){
36247                 return h;
36248             }
36249             h = h.nextSibling;
36250         }
36251         return null;
36252     },
36253
36254     prevVisible : function(h){
36255         var v = this.view, cm = this.grid.colModel;
36256         h = h.prevSibling;
36257         while(h){
36258             if(!cm.isHidden(v.getCellIndex(h))){
36259                 return h;
36260             }
36261             h = h.prevSibling;
36262         }
36263         return null;
36264     },
36265
36266     positionIndicator : function(h, n, e){
36267         var x = Roo.lib.Event.getPageX(e);
36268         var r = Roo.lib.Dom.getRegion(n.firstChild);
36269         var px, pt, py = r.top + this.proxyOffsets[1];
36270         if((r.right - x) <= (r.right-r.left)/2){
36271             px = r.right+this.view.borderWidth;
36272             pt = "after";
36273         }else{
36274             px = r.left;
36275             pt = "before";
36276         }
36277         var oldIndex = this.view.getCellIndex(h);
36278         var newIndex = this.view.getCellIndex(n);
36279
36280         if(this.grid.colModel.isFixed(newIndex)){
36281             return false;
36282         }
36283
36284         var locked = this.grid.colModel.isLocked(newIndex);
36285
36286         if(pt == "after"){
36287             newIndex++;
36288         }
36289         if(oldIndex < newIndex){
36290             newIndex--;
36291         }
36292         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36293             return false;
36294         }
36295         px +=  this.proxyOffsets[0];
36296         this.proxyTop.setLeftTop(px, py);
36297         this.proxyTop.show();
36298         if(!this.bottomOffset){
36299             this.bottomOffset = this.view.mainHd.getHeight();
36300         }
36301         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36302         this.proxyBottom.show();
36303         return pt;
36304     },
36305
36306     onNodeEnter : function(n, dd, e, data){
36307         if(data.header != n){
36308             this.positionIndicator(data.header, n, e);
36309         }
36310     },
36311
36312     onNodeOver : function(n, dd, e, data){
36313         var result = false;
36314         if(data.header != n){
36315             result = this.positionIndicator(data.header, n, e);
36316         }
36317         if(!result){
36318             this.proxyTop.hide();
36319             this.proxyBottom.hide();
36320         }
36321         return result ? this.dropAllowed : this.dropNotAllowed;
36322     },
36323
36324     onNodeOut : function(n, dd, e, data){
36325         this.proxyTop.hide();
36326         this.proxyBottom.hide();
36327     },
36328
36329     onNodeDrop : function(n, dd, e, data){
36330         var h = data.header;
36331         if(h != n){
36332             var cm = this.grid.colModel;
36333             var x = Roo.lib.Event.getPageX(e);
36334             var r = Roo.lib.Dom.getRegion(n.firstChild);
36335             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36336             var oldIndex = this.view.getCellIndex(h);
36337             var newIndex = this.view.getCellIndex(n);
36338             var locked = cm.isLocked(newIndex);
36339             if(pt == "after"){
36340                 newIndex++;
36341             }
36342             if(oldIndex < newIndex){
36343                 newIndex--;
36344             }
36345             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36346                 return false;
36347             }
36348             cm.setLocked(oldIndex, locked, true);
36349             cm.moveColumn(oldIndex, newIndex);
36350             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36351             return true;
36352         }
36353         return false;
36354     }
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 /**
36368  * @class Roo.grid.GridView
36369  * @extends Roo.util.Observable
36370  *
36371  * @constructor
36372  * @param {Object} config
36373  */
36374 Roo.grid.GridView = function(config){
36375     Roo.grid.GridView.superclass.constructor.call(this);
36376     this.el = null;
36377
36378     Roo.apply(this, config);
36379 };
36380
36381 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36382
36383     unselectable :  'unselectable="on"',
36384     unselectableCls :  'x-unselectable',
36385     
36386     
36387     rowClass : "x-grid-row",
36388
36389     cellClass : "x-grid-col",
36390
36391     tdClass : "x-grid-td",
36392
36393     hdClass : "x-grid-hd",
36394
36395     splitClass : "x-grid-split",
36396
36397     sortClasses : ["sort-asc", "sort-desc"],
36398
36399     enableMoveAnim : false,
36400
36401     hlColor: "C3DAF9",
36402
36403     dh : Roo.DomHelper,
36404
36405     fly : Roo.Element.fly,
36406
36407     css : Roo.util.CSS,
36408
36409     borderWidth: 1,
36410
36411     splitOffset: 3,
36412
36413     scrollIncrement : 22,
36414
36415     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36416
36417     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36418
36419     bind : function(ds, cm){
36420         if(this.ds){
36421             this.ds.un("load", this.onLoad, this);
36422             this.ds.un("datachanged", this.onDataChange, this);
36423             this.ds.un("add", this.onAdd, this);
36424             this.ds.un("remove", this.onRemove, this);
36425             this.ds.un("update", this.onUpdate, this);
36426             this.ds.un("clear", this.onClear, this);
36427         }
36428         if(ds){
36429             ds.on("load", this.onLoad, this);
36430             ds.on("datachanged", this.onDataChange, this);
36431             ds.on("add", this.onAdd, this);
36432             ds.on("remove", this.onRemove, this);
36433             ds.on("update", this.onUpdate, this);
36434             ds.on("clear", this.onClear, this);
36435         }
36436         this.ds = ds;
36437
36438         if(this.cm){
36439             this.cm.un("widthchange", this.onColWidthChange, this);
36440             this.cm.un("headerchange", this.onHeaderChange, this);
36441             this.cm.un("hiddenchange", this.onHiddenChange, this);
36442             this.cm.un("columnmoved", this.onColumnMove, this);
36443             this.cm.un("columnlockchange", this.onColumnLock, this);
36444         }
36445         if(cm){
36446             this.generateRules(cm);
36447             cm.on("widthchange", this.onColWidthChange, this);
36448             cm.on("headerchange", this.onHeaderChange, this);
36449             cm.on("hiddenchange", this.onHiddenChange, this);
36450             cm.on("columnmoved", this.onColumnMove, this);
36451             cm.on("columnlockchange", this.onColumnLock, this);
36452         }
36453         this.cm = cm;
36454     },
36455
36456     init: function(grid){
36457         Roo.grid.GridView.superclass.init.call(this, grid);
36458
36459         this.bind(grid.dataSource, grid.colModel);
36460
36461         grid.on("headerclick", this.handleHeaderClick, this);
36462
36463         if(grid.trackMouseOver){
36464             grid.on("mouseover", this.onRowOver, this);
36465             grid.on("mouseout", this.onRowOut, this);
36466         }
36467         grid.cancelTextSelection = function(){};
36468         this.gridId = grid.id;
36469
36470         var tpls = this.templates || {};
36471
36472         if(!tpls.master){
36473             tpls.master = new Roo.Template(
36474                '<div class="x-grid" hidefocus="true">',
36475                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36476                   '<div class="x-grid-topbar"></div>',
36477                   '<div class="x-grid-scroller"><div></div></div>',
36478                   '<div class="x-grid-locked">',
36479                       '<div class="x-grid-header">{lockedHeader}</div>',
36480                       '<div class="x-grid-body">{lockedBody}</div>',
36481                   "</div>",
36482                   '<div class="x-grid-viewport">',
36483                       '<div class="x-grid-header">{header}</div>',
36484                       '<div class="x-grid-body">{body}</div>',
36485                   "</div>",
36486                   '<div class="x-grid-bottombar"></div>',
36487                  
36488                   '<div class="x-grid-resize-proxy">&#160;</div>',
36489                "</div>"
36490             );
36491             tpls.master.disableformats = true;
36492         }
36493
36494         if(!tpls.header){
36495             tpls.header = new Roo.Template(
36496                '<table border="0" cellspacing="0" cellpadding="0">',
36497                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36498                "</table>{splits}"
36499             );
36500             tpls.header.disableformats = true;
36501         }
36502         tpls.header.compile();
36503
36504         if(!tpls.hcell){
36505             tpls.hcell = new Roo.Template(
36506                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36507                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36508                 "</div></td>"
36509              );
36510              tpls.hcell.disableFormats = true;
36511         }
36512         tpls.hcell.compile();
36513
36514         if(!tpls.hsplit){
36515             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36516                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36517             tpls.hsplit.disableFormats = true;
36518         }
36519         tpls.hsplit.compile();
36520
36521         if(!tpls.body){
36522             tpls.body = new Roo.Template(
36523                '<table border="0" cellspacing="0" cellpadding="0">',
36524                "<tbody>{rows}</tbody>",
36525                "</table>"
36526             );
36527             tpls.body.disableFormats = true;
36528         }
36529         tpls.body.compile();
36530
36531         if(!tpls.row){
36532             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36533             tpls.row.disableFormats = true;
36534         }
36535         tpls.row.compile();
36536
36537         if(!tpls.cell){
36538             tpls.cell = new Roo.Template(
36539                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36540                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36541                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36542                 "</td>"
36543             );
36544             tpls.cell.disableFormats = true;
36545         }
36546         tpls.cell.compile();
36547
36548         this.templates = tpls;
36549     },
36550
36551     // remap these for backwards compat
36552     onColWidthChange : function(){
36553         this.updateColumns.apply(this, arguments);
36554     },
36555     onHeaderChange : function(){
36556         this.updateHeaders.apply(this, arguments);
36557     }, 
36558     onHiddenChange : function(){
36559         this.handleHiddenChange.apply(this, arguments);
36560     },
36561     onColumnMove : function(){
36562         this.handleColumnMove.apply(this, arguments);
36563     },
36564     onColumnLock : function(){
36565         this.handleLockChange.apply(this, arguments);
36566     },
36567
36568     onDataChange : function(){
36569         this.refresh();
36570         this.updateHeaderSortState();
36571     },
36572
36573     onClear : function(){
36574         this.refresh();
36575     },
36576
36577     onUpdate : function(ds, record){
36578         this.refreshRow(record);
36579     },
36580
36581     refreshRow : function(record){
36582         var ds = this.ds, index;
36583         if(typeof record == 'number'){
36584             index = record;
36585             record = ds.getAt(index);
36586         }else{
36587             index = ds.indexOf(record);
36588         }
36589         this.insertRows(ds, index, index, true);
36590         this.onRemove(ds, record, index+1, true);
36591         this.syncRowHeights(index, index);
36592         this.layout();
36593         this.fireEvent("rowupdated", this, index, record);
36594     },
36595
36596     onAdd : function(ds, records, index){
36597         this.insertRows(ds, index, index + (records.length-1));
36598     },
36599
36600     onRemove : function(ds, record, index, isUpdate){
36601         if(isUpdate !== true){
36602             this.fireEvent("beforerowremoved", this, index, record);
36603         }
36604         var bt = this.getBodyTable(), lt = this.getLockedTable();
36605         if(bt.rows[index]){
36606             bt.firstChild.removeChild(bt.rows[index]);
36607         }
36608         if(lt.rows[index]){
36609             lt.firstChild.removeChild(lt.rows[index]);
36610         }
36611         if(isUpdate !== true){
36612             this.stripeRows(index);
36613             this.syncRowHeights(index, index);
36614             this.layout();
36615             this.fireEvent("rowremoved", this, index, record);
36616         }
36617     },
36618
36619     onLoad : function(){
36620         this.scrollToTop();
36621     },
36622
36623     /**
36624      * Scrolls the grid to the top
36625      */
36626     scrollToTop : function(){
36627         if(this.scroller){
36628             this.scroller.dom.scrollTop = 0;
36629             this.syncScroll();
36630         }
36631     },
36632
36633     /**
36634      * Gets a panel in the header of the grid that can be used for toolbars etc.
36635      * After modifying the contents of this panel a call to grid.autoSize() may be
36636      * required to register any changes in size.
36637      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36638      * @return Roo.Element
36639      */
36640     getHeaderPanel : function(doShow){
36641         if(doShow){
36642             this.headerPanel.show();
36643         }
36644         return this.headerPanel;
36645     },
36646
36647     /**
36648      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36649      * After modifying the contents of this panel a call to grid.autoSize() may be
36650      * required to register any changes in size.
36651      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36652      * @return Roo.Element
36653      */
36654     getFooterPanel : function(doShow){
36655         if(doShow){
36656             this.footerPanel.show();
36657         }
36658         return this.footerPanel;
36659     },
36660
36661     initElements : function(){
36662         var E = Roo.Element;
36663         var el = this.grid.getGridEl().dom.firstChild;
36664         var cs = el.childNodes;
36665
36666         this.el = new E(el);
36667         
36668          this.focusEl = new E(el.firstChild);
36669         this.focusEl.swallowEvent("click", true);
36670         
36671         this.headerPanel = new E(cs[1]);
36672         this.headerPanel.enableDisplayMode("block");
36673
36674         this.scroller = new E(cs[2]);
36675         this.scrollSizer = new E(this.scroller.dom.firstChild);
36676
36677         this.lockedWrap = new E(cs[3]);
36678         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36679         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36680
36681         this.mainWrap = new E(cs[4]);
36682         this.mainHd = new E(this.mainWrap.dom.firstChild);
36683         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36684
36685         this.footerPanel = new E(cs[5]);
36686         this.footerPanel.enableDisplayMode("block");
36687
36688         this.resizeProxy = new E(cs[6]);
36689
36690         this.headerSelector = String.format(
36691            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36692            this.lockedHd.id, this.mainHd.id
36693         );
36694
36695         this.splitterSelector = String.format(
36696            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36697            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36698         );
36699     },
36700     idToCssName : function(s)
36701     {
36702         return s.replace(/[^a-z0-9]+/ig, '-');
36703     },
36704
36705     getHeaderCell : function(index){
36706         return Roo.DomQuery.select(this.headerSelector)[index];
36707     },
36708
36709     getHeaderCellMeasure : function(index){
36710         return this.getHeaderCell(index).firstChild;
36711     },
36712
36713     getHeaderCellText : function(index){
36714         return this.getHeaderCell(index).firstChild.firstChild;
36715     },
36716
36717     getLockedTable : function(){
36718         return this.lockedBody.dom.firstChild;
36719     },
36720
36721     getBodyTable : function(){
36722         return this.mainBody.dom.firstChild;
36723     },
36724
36725     getLockedRow : function(index){
36726         return this.getLockedTable().rows[index];
36727     },
36728
36729     getRow : function(index){
36730         return this.getBodyTable().rows[index];
36731     },
36732
36733     getRowComposite : function(index){
36734         if(!this.rowEl){
36735             this.rowEl = new Roo.CompositeElementLite();
36736         }
36737         var els = [], lrow, mrow;
36738         if(lrow = this.getLockedRow(index)){
36739             els.push(lrow);
36740         }
36741         if(mrow = this.getRow(index)){
36742             els.push(mrow);
36743         }
36744         this.rowEl.elements = els;
36745         return this.rowEl;
36746     },
36747     /**
36748      * Gets the 'td' of the cell
36749      * 
36750      * @param {Integer} rowIndex row to select
36751      * @param {Integer} colIndex column to select
36752      * 
36753      * @return {Object} 
36754      */
36755     getCell : function(rowIndex, colIndex){
36756         var locked = this.cm.getLockedCount();
36757         var source;
36758         if(colIndex < locked){
36759             source = this.lockedBody.dom.firstChild;
36760         }else{
36761             source = this.mainBody.dom.firstChild;
36762             colIndex -= locked;
36763         }
36764         return source.rows[rowIndex].childNodes[colIndex];
36765     },
36766
36767     getCellText : function(rowIndex, colIndex){
36768         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36769     },
36770
36771     getCellBox : function(cell){
36772         var b = this.fly(cell).getBox();
36773         if(Roo.isOpera){ // opera fails to report the Y
36774             b.y = cell.offsetTop + this.mainBody.getY();
36775         }
36776         return b;
36777     },
36778
36779     getCellIndex : function(cell){
36780         var id = String(cell.className).match(this.cellRE);
36781         if(id){
36782             return parseInt(id[1], 10);
36783         }
36784         return 0;
36785     },
36786
36787     findHeaderIndex : function(n){
36788         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36789         return r ? this.getCellIndex(r) : false;
36790     },
36791
36792     findHeaderCell : function(n){
36793         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36794         return r ? r : false;
36795     },
36796
36797     findRowIndex : function(n){
36798         if(!n){
36799             return false;
36800         }
36801         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36802         return r ? r.rowIndex : false;
36803     },
36804
36805     findCellIndex : function(node){
36806         var stop = this.el.dom;
36807         while(node && node != stop){
36808             if(this.findRE.test(node.className)){
36809                 return this.getCellIndex(node);
36810             }
36811             node = node.parentNode;
36812         }
36813         return false;
36814     },
36815
36816     getColumnId : function(index){
36817         return this.cm.getColumnId(index);
36818     },
36819
36820     getSplitters : function()
36821     {
36822         if(this.splitterSelector){
36823            return Roo.DomQuery.select(this.splitterSelector);
36824         }else{
36825             return null;
36826       }
36827     },
36828
36829     getSplitter : function(index){
36830         return this.getSplitters()[index];
36831     },
36832
36833     onRowOver : function(e, t){
36834         var row;
36835         if((row = this.findRowIndex(t)) !== false){
36836             this.getRowComposite(row).addClass("x-grid-row-over");
36837         }
36838     },
36839
36840     onRowOut : function(e, t){
36841         var row;
36842         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36843             this.getRowComposite(row).removeClass("x-grid-row-over");
36844         }
36845     },
36846
36847     renderHeaders : function(){
36848         var cm = this.cm;
36849         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36850         var cb = [], lb = [], sb = [], lsb = [], p = {};
36851         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36852             p.cellId = "x-grid-hd-0-" + i;
36853             p.splitId = "x-grid-csplit-0-" + i;
36854             p.id = cm.getColumnId(i);
36855             p.title = cm.getColumnTooltip(i) || "";
36856             p.value = cm.getColumnHeader(i) || "";
36857             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36858             if(!cm.isLocked(i)){
36859                 cb[cb.length] = ct.apply(p);
36860                 sb[sb.length] = st.apply(p);
36861             }else{
36862                 lb[lb.length] = ct.apply(p);
36863                 lsb[lsb.length] = st.apply(p);
36864             }
36865         }
36866         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36867                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36868     },
36869
36870     updateHeaders : function(){
36871         var html = this.renderHeaders();
36872         this.lockedHd.update(html[0]);
36873         this.mainHd.update(html[1]);
36874     },
36875
36876     /**
36877      * Focuses the specified row.
36878      * @param {Number} row The row index
36879      */
36880     focusRow : function(row)
36881     {
36882         //Roo.log('GridView.focusRow');
36883         var x = this.scroller.dom.scrollLeft;
36884         this.focusCell(row, 0, false);
36885         this.scroller.dom.scrollLeft = x;
36886     },
36887
36888     /**
36889      * Focuses the specified cell.
36890      * @param {Number} row The row index
36891      * @param {Number} col The column index
36892      * @param {Boolean} hscroll false to disable horizontal scrolling
36893      */
36894     focusCell : function(row, col, hscroll)
36895     {
36896         //Roo.log('GridView.focusCell');
36897         var el = this.ensureVisible(row, col, hscroll);
36898         this.focusEl.alignTo(el, "tl-tl");
36899         if(Roo.isGecko){
36900             this.focusEl.focus();
36901         }else{
36902             this.focusEl.focus.defer(1, this.focusEl);
36903         }
36904     },
36905
36906     /**
36907      * Scrolls the specified cell into view
36908      * @param {Number} row The row index
36909      * @param {Number} col The column index
36910      * @param {Boolean} hscroll false to disable horizontal scrolling
36911      */
36912     ensureVisible : function(row, col, hscroll)
36913     {
36914         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36915         //return null; //disable for testing.
36916         if(typeof row != "number"){
36917             row = row.rowIndex;
36918         }
36919         if(row < 0 && row >= this.ds.getCount()){
36920             return  null;
36921         }
36922         col = (col !== undefined ? col : 0);
36923         var cm = this.grid.colModel;
36924         while(cm.isHidden(col)){
36925             col++;
36926         }
36927
36928         var el = this.getCell(row, col);
36929         if(!el){
36930             return null;
36931         }
36932         var c = this.scroller.dom;
36933
36934         var ctop = parseInt(el.offsetTop, 10);
36935         var cleft = parseInt(el.offsetLeft, 10);
36936         var cbot = ctop + el.offsetHeight;
36937         var cright = cleft + el.offsetWidth;
36938         
36939         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36940         var stop = parseInt(c.scrollTop, 10);
36941         var sleft = parseInt(c.scrollLeft, 10);
36942         var sbot = stop + ch;
36943         var sright = sleft + c.clientWidth;
36944         /*
36945         Roo.log('GridView.ensureVisible:' +
36946                 ' ctop:' + ctop +
36947                 ' c.clientHeight:' + c.clientHeight +
36948                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36949                 ' stop:' + stop +
36950                 ' cbot:' + cbot +
36951                 ' sbot:' + sbot +
36952                 ' ch:' + ch  
36953                 );
36954         */
36955         if(ctop < stop){
36956              c.scrollTop = ctop;
36957             //Roo.log("set scrolltop to ctop DISABLE?");
36958         }else if(cbot > sbot){
36959             //Roo.log("set scrolltop to cbot-ch");
36960             c.scrollTop = cbot-ch;
36961         }
36962         
36963         if(hscroll !== false){
36964             if(cleft < sleft){
36965                 c.scrollLeft = cleft;
36966             }else if(cright > sright){
36967                 c.scrollLeft = cright-c.clientWidth;
36968             }
36969         }
36970          
36971         return el;
36972     },
36973
36974     updateColumns : function(){
36975         this.grid.stopEditing();
36976         var cm = this.grid.colModel, colIds = this.getColumnIds();
36977         //var totalWidth = cm.getTotalWidth();
36978         var pos = 0;
36979         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36980             //if(cm.isHidden(i)) continue;
36981             var w = cm.getColumnWidth(i);
36982             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36983             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36984         }
36985         this.updateSplitters();
36986     },
36987
36988     generateRules : function(cm){
36989         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
36990         Roo.util.CSS.removeStyleSheet(rulesId);
36991         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36992             var cid = cm.getColumnId(i);
36993             var align = '';
36994             if(cm.config[i].align){
36995                 align = 'text-align:'+cm.config[i].align+';';
36996             }
36997             var hidden = '';
36998             if(cm.isHidden(i)){
36999                 hidden = 'display:none;';
37000             }
37001             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37002             ruleBuf.push(
37003                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37004                     this.hdSelector, cid, " {\n", align, width, "}\n",
37005                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37006                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37007         }
37008         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37009     },
37010
37011     updateSplitters : function(){
37012         var cm = this.cm, s = this.getSplitters();
37013         if(s){ // splitters not created yet
37014             var pos = 0, locked = true;
37015             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37016                 if(cm.isHidden(i)) continue;
37017                 var w = cm.getColumnWidth(i); // make sure it's a number
37018                 if(!cm.isLocked(i) && locked){
37019                     pos = 0;
37020                     locked = false;
37021                 }
37022                 pos += w;
37023                 s[i].style.left = (pos-this.splitOffset) + "px";
37024             }
37025         }
37026     },
37027
37028     handleHiddenChange : function(colModel, colIndex, hidden){
37029         if(hidden){
37030             this.hideColumn(colIndex);
37031         }else{
37032             this.unhideColumn(colIndex);
37033         }
37034     },
37035
37036     hideColumn : function(colIndex){
37037         var cid = this.getColumnId(colIndex);
37038         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37039         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37040         if(Roo.isSafari){
37041             this.updateHeaders();
37042         }
37043         this.updateSplitters();
37044         this.layout();
37045     },
37046
37047     unhideColumn : function(colIndex){
37048         var cid = this.getColumnId(colIndex);
37049         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37050         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37051
37052         if(Roo.isSafari){
37053             this.updateHeaders();
37054         }
37055         this.updateSplitters();
37056         this.layout();
37057     },
37058
37059     insertRows : function(dm, firstRow, lastRow, isUpdate){
37060         if(firstRow == 0 && lastRow == dm.getCount()-1){
37061             this.refresh();
37062         }else{
37063             if(!isUpdate){
37064                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37065             }
37066             var s = this.getScrollState();
37067             var markup = this.renderRows(firstRow, lastRow);
37068             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37069             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37070             this.restoreScroll(s);
37071             if(!isUpdate){
37072                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37073                 this.syncRowHeights(firstRow, lastRow);
37074                 this.stripeRows(firstRow);
37075                 this.layout();
37076             }
37077         }
37078     },
37079
37080     bufferRows : function(markup, target, index){
37081         var before = null, trows = target.rows, tbody = target.tBodies[0];
37082         if(index < trows.length){
37083             before = trows[index];
37084         }
37085         var b = document.createElement("div");
37086         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37087         var rows = b.firstChild.rows;
37088         for(var i = 0, len = rows.length; i < len; i++){
37089             if(before){
37090                 tbody.insertBefore(rows[0], before);
37091             }else{
37092                 tbody.appendChild(rows[0]);
37093             }
37094         }
37095         b.innerHTML = "";
37096         b = null;
37097     },
37098
37099     deleteRows : function(dm, firstRow, lastRow){
37100         if(dm.getRowCount()<1){
37101             this.fireEvent("beforerefresh", this);
37102             this.mainBody.update("");
37103             this.lockedBody.update("");
37104             this.fireEvent("refresh", this);
37105         }else{
37106             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37107             var bt = this.getBodyTable();
37108             var tbody = bt.firstChild;
37109             var rows = bt.rows;
37110             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37111                 tbody.removeChild(rows[firstRow]);
37112             }
37113             this.stripeRows(firstRow);
37114             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37115         }
37116     },
37117
37118     updateRows : function(dataSource, firstRow, lastRow){
37119         var s = this.getScrollState();
37120         this.refresh();
37121         this.restoreScroll(s);
37122     },
37123
37124     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37125         if(!noRefresh){
37126            this.refresh();
37127         }
37128         this.updateHeaderSortState();
37129     },
37130
37131     getScrollState : function(){
37132         
37133         var sb = this.scroller.dom;
37134         return {left: sb.scrollLeft, top: sb.scrollTop};
37135     },
37136
37137     stripeRows : function(startRow){
37138         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37139             return;
37140         }
37141         startRow = startRow || 0;
37142         var rows = this.getBodyTable().rows;
37143         var lrows = this.getLockedTable().rows;
37144         var cls = ' x-grid-row-alt ';
37145         for(var i = startRow, len = rows.length; i < len; i++){
37146             var row = rows[i], lrow = lrows[i];
37147             var isAlt = ((i+1) % 2 == 0);
37148             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37149             if(isAlt == hasAlt){
37150                 continue;
37151             }
37152             if(isAlt){
37153                 row.className += " x-grid-row-alt";
37154             }else{
37155                 row.className = row.className.replace("x-grid-row-alt", "");
37156             }
37157             if(lrow){
37158                 lrow.className = row.className;
37159             }
37160         }
37161     },
37162
37163     restoreScroll : function(state){
37164         //Roo.log('GridView.restoreScroll');
37165         var sb = this.scroller.dom;
37166         sb.scrollLeft = state.left;
37167         sb.scrollTop = state.top;
37168         this.syncScroll();
37169     },
37170
37171     syncScroll : function(){
37172         //Roo.log('GridView.syncScroll');
37173         var sb = this.scroller.dom;
37174         var sh = this.mainHd.dom;
37175         var bs = this.mainBody.dom;
37176         var lv = this.lockedBody.dom;
37177         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37178         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37179     },
37180
37181     handleScroll : function(e){
37182         this.syncScroll();
37183         var sb = this.scroller.dom;
37184         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37185         e.stopEvent();
37186     },
37187
37188     handleWheel : function(e){
37189         var d = e.getWheelDelta();
37190         this.scroller.dom.scrollTop -= d*22;
37191         // set this here to prevent jumpy scrolling on large tables
37192         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37193         e.stopEvent();
37194     },
37195
37196     renderRows : function(startRow, endRow){
37197         // pull in all the crap needed to render rows
37198         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37199         var colCount = cm.getColumnCount();
37200
37201         if(ds.getCount() < 1){
37202             return ["", ""];
37203         }
37204
37205         // build a map for all the columns
37206         var cs = [];
37207         for(var i = 0; i < colCount; i++){
37208             var name = cm.getDataIndex(i);
37209             cs[i] = {
37210                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37211                 renderer : cm.getRenderer(i),
37212                 id : cm.getColumnId(i),
37213                 locked : cm.isLocked(i)
37214             };
37215         }
37216
37217         startRow = startRow || 0;
37218         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37219
37220         // records to render
37221         var rs = ds.getRange(startRow, endRow);
37222
37223         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37224     },
37225
37226     // As much as I hate to duplicate code, this was branched because FireFox really hates
37227     // [].join("") on strings. The performance difference was substantial enough to
37228     // branch this function
37229     doRender : Roo.isGecko ?
37230             function(cs, rs, ds, startRow, colCount, stripe){
37231                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37232                 // buffers
37233                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37234                 
37235                 var hasListener = this.grid.hasListener('rowclass');
37236                 var rowcfg = {};
37237                 for(var j = 0, len = rs.length; j < len; j++){
37238                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37239                     for(var i = 0; i < colCount; i++){
37240                         c = cs[i];
37241                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37242                         p.id = c.id;
37243                         p.css = p.attr = "";
37244                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37245                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37246                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37247                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37248                         }
37249                         var markup = ct.apply(p);
37250                         if(!c.locked){
37251                             cb+= markup;
37252                         }else{
37253                             lcb+= markup;
37254                         }
37255                     }
37256                     var alt = [];
37257                     if(stripe && ((rowIndex+1) % 2 == 0)){
37258                         alt.push("x-grid-row-alt")
37259                     }
37260                     if(r.dirty){
37261                         alt.push(  " x-grid-dirty-row");
37262                     }
37263                     rp.cells = lcb;
37264                     if(this.getRowClass){
37265                         alt.push(this.getRowClass(r, rowIndex));
37266                     }
37267                     if (hasListener) {
37268                         rowcfg = {
37269                              
37270                             record: r,
37271                             rowIndex : rowIndex,
37272                             rowClass : ''
37273                         }
37274                         this.grid.fireEvent('rowclass', this, rowcfg);
37275                         alt.push(rowcfg.rowClass);
37276                     }
37277                     rp.alt = alt.join(" ");
37278                     lbuf+= rt.apply(rp);
37279                     rp.cells = cb;
37280                     buf+=  rt.apply(rp);
37281                 }
37282                 return [lbuf, buf];
37283             } :
37284             function(cs, rs, ds, startRow, colCount, stripe){
37285                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37286                 // buffers
37287                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37288                 var hasListener = this.grid.hasListener('rowclass');
37289  
37290                 var rowcfg = {};
37291                 for(var j = 0, len = rs.length; j < len; j++){
37292                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37293                     for(var i = 0; i < colCount; i++){
37294                         c = cs[i];
37295                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37296                         p.id = c.id;
37297                         p.css = p.attr = "";
37298                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37299                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37300                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37301                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37302                         }
37303                         
37304                         var markup = ct.apply(p);
37305                         if(!c.locked){
37306                             cb[cb.length] = markup;
37307                         }else{
37308                             lcb[lcb.length] = markup;
37309                         }
37310                     }
37311                     var alt = [];
37312                     if(stripe && ((rowIndex+1) % 2 == 0)){
37313                         alt.push( "x-grid-row-alt");
37314                     }
37315                     if(r.dirty){
37316                         alt.push(" x-grid-dirty-row");
37317                     }
37318                     rp.cells = lcb;
37319                     if(this.getRowClass){
37320                         alt.push( this.getRowClass(r, rowIndex));
37321                     }
37322                     if (hasListener) {
37323                         rowcfg = {
37324                              
37325                             record: r,
37326                             rowIndex : rowIndex,
37327                             rowClass : ''
37328                         }
37329                         this.grid.fireEvent('rowclass', this, rowcfg);
37330                         alt.push(rowcfg.rowClass);
37331                     }
37332                     rp.alt = alt.join(" ");
37333                     rp.cells = lcb.join("");
37334                     lbuf[lbuf.length] = rt.apply(rp);
37335                     rp.cells = cb.join("");
37336                     buf[buf.length] =  rt.apply(rp);
37337                 }
37338                 return [lbuf.join(""), buf.join("")];
37339             },
37340
37341     renderBody : function(){
37342         var markup = this.renderRows();
37343         var bt = this.templates.body;
37344         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37345     },
37346
37347     /**
37348      * Refreshes the grid
37349      * @param {Boolean} headersToo
37350      */
37351     refresh : function(headersToo){
37352         this.fireEvent("beforerefresh", this);
37353         this.grid.stopEditing();
37354         var result = this.renderBody();
37355         this.lockedBody.update(result[0]);
37356         this.mainBody.update(result[1]);
37357         if(headersToo === true){
37358             this.updateHeaders();
37359             this.updateColumns();
37360             this.updateSplitters();
37361             this.updateHeaderSortState();
37362         }
37363         this.syncRowHeights();
37364         this.layout();
37365         this.fireEvent("refresh", this);
37366     },
37367
37368     handleColumnMove : function(cm, oldIndex, newIndex){
37369         this.indexMap = null;
37370         var s = this.getScrollState();
37371         this.refresh(true);
37372         this.restoreScroll(s);
37373         this.afterMove(newIndex);
37374     },
37375
37376     afterMove : function(colIndex){
37377         if(this.enableMoveAnim && Roo.enableFx){
37378             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37379         }
37380         // if multisort - fix sortOrder, and reload..
37381         if (this.grid.dataSource.multiSort) {
37382             // the we can call sort again..
37383             var dm = this.grid.dataSource;
37384             var cm = this.grid.colModel;
37385             var so = [];
37386             for(var i = 0; i < cm.config.length; i++ ) {
37387                 
37388                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37389                     continue; // dont' bother, it's not in sort list or being set.
37390                 }
37391                 
37392                 so.push(cm.config[i].dataIndex);
37393             };
37394             dm.sortOrder = so;
37395             dm.load(dm.lastOptions);
37396             
37397             
37398         }
37399         
37400     },
37401
37402     updateCell : function(dm, rowIndex, dataIndex){
37403         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37404         if(typeof colIndex == "undefined"){ // not present in grid
37405             return;
37406         }
37407         var cm = this.grid.colModel;
37408         var cell = this.getCell(rowIndex, colIndex);
37409         var cellText = this.getCellText(rowIndex, colIndex);
37410
37411         var p = {
37412             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37413             id : cm.getColumnId(colIndex),
37414             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37415         };
37416         var renderer = cm.getRenderer(colIndex);
37417         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37418         if(typeof val == "undefined" || val === "") val = "&#160;";
37419         cellText.innerHTML = val;
37420         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37421         this.syncRowHeights(rowIndex, rowIndex);
37422     },
37423
37424     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37425         var maxWidth = 0;
37426         if(this.grid.autoSizeHeaders){
37427             var h = this.getHeaderCellMeasure(colIndex);
37428             maxWidth = Math.max(maxWidth, h.scrollWidth);
37429         }
37430         var tb, index;
37431         if(this.cm.isLocked(colIndex)){
37432             tb = this.getLockedTable();
37433             index = colIndex;
37434         }else{
37435             tb = this.getBodyTable();
37436             index = colIndex - this.cm.getLockedCount();
37437         }
37438         if(tb && tb.rows){
37439             var rows = tb.rows;
37440             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37441             for(var i = 0; i < stopIndex; i++){
37442                 var cell = rows[i].childNodes[index].firstChild;
37443                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37444             }
37445         }
37446         return maxWidth + /*margin for error in IE*/ 5;
37447     },
37448     /**
37449      * Autofit a column to its content.
37450      * @param {Number} colIndex
37451      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37452      */
37453      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37454          if(this.cm.isHidden(colIndex)){
37455              return; // can't calc a hidden column
37456          }
37457         if(forceMinSize){
37458             var cid = this.cm.getColumnId(colIndex);
37459             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37460            if(this.grid.autoSizeHeaders){
37461                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37462            }
37463         }
37464         var newWidth = this.calcColumnWidth(colIndex);
37465         this.cm.setColumnWidth(colIndex,
37466             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37467         if(!suppressEvent){
37468             this.grid.fireEvent("columnresize", colIndex, newWidth);
37469         }
37470     },
37471
37472     /**
37473      * Autofits all columns to their content and then expands to fit any extra space in the grid
37474      */
37475      autoSizeColumns : function(){
37476         var cm = this.grid.colModel;
37477         var colCount = cm.getColumnCount();
37478         for(var i = 0; i < colCount; i++){
37479             this.autoSizeColumn(i, true, true);
37480         }
37481         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37482             this.fitColumns();
37483         }else{
37484             this.updateColumns();
37485             this.layout();
37486         }
37487     },
37488
37489     /**
37490      * Autofits all columns to the grid's width proportionate with their current size
37491      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37492      */
37493     fitColumns : function(reserveScrollSpace){
37494         var cm = this.grid.colModel;
37495         var colCount = cm.getColumnCount();
37496         var cols = [];
37497         var width = 0;
37498         var i, w;
37499         for (i = 0; i < colCount; i++){
37500             if(!cm.isHidden(i) && !cm.isFixed(i)){
37501                 w = cm.getColumnWidth(i);
37502                 cols.push(i);
37503                 cols.push(w);
37504                 width += w;
37505             }
37506         }
37507         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37508         if(reserveScrollSpace){
37509             avail -= 17;
37510         }
37511         var frac = (avail - cm.getTotalWidth())/width;
37512         while (cols.length){
37513             w = cols.pop();
37514             i = cols.pop();
37515             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37516         }
37517         this.updateColumns();
37518         this.layout();
37519     },
37520
37521     onRowSelect : function(rowIndex){
37522         var row = this.getRowComposite(rowIndex);
37523         row.addClass("x-grid-row-selected");
37524     },
37525
37526     onRowDeselect : function(rowIndex){
37527         var row = this.getRowComposite(rowIndex);
37528         row.removeClass("x-grid-row-selected");
37529     },
37530
37531     onCellSelect : function(row, col){
37532         var cell = this.getCell(row, col);
37533         if(cell){
37534             Roo.fly(cell).addClass("x-grid-cell-selected");
37535         }
37536     },
37537
37538     onCellDeselect : function(row, col){
37539         var cell = this.getCell(row, col);
37540         if(cell){
37541             Roo.fly(cell).removeClass("x-grid-cell-selected");
37542         }
37543     },
37544
37545     updateHeaderSortState : function(){
37546         
37547         // sort state can be single { field: xxx, direction : yyy}
37548         // or   { xxx=>ASC , yyy : DESC ..... }
37549         
37550         var mstate = {};
37551         if (!this.ds.multiSort) { 
37552             var state = this.ds.getSortState();
37553             if(!state){
37554                 return;
37555             }
37556             mstate[state.field] = state.direction;
37557             // FIXME... - this is not used here.. but might be elsewhere..
37558             this.sortState = state;
37559             
37560         } else {
37561             mstate = this.ds.sortToggle;
37562         }
37563         //remove existing sort classes..
37564         
37565         var sc = this.sortClasses;
37566         var hds = this.el.select(this.headerSelector).removeClass(sc);
37567         
37568         for(var f in mstate) {
37569         
37570             var sortColumn = this.cm.findColumnIndex(f);
37571             
37572             if(sortColumn != -1){
37573                 var sortDir = mstate[f];        
37574                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37575             }
37576         }
37577         
37578          
37579         
37580     },
37581
37582
37583     handleHeaderClick : function(g, index,e){
37584         
37585         Roo.log("header click");
37586         
37587         if (Roo.isTouch) {
37588             // touch events on header are handled by context
37589             this.handleHdCtx(g,index,e);
37590             return;
37591         }
37592         
37593         
37594         if(this.headersDisabled){
37595             return;
37596         }
37597         var dm = g.dataSource, cm = g.colModel;
37598         if(!cm.isSortable(index)){
37599             return;
37600         }
37601         g.stopEditing();
37602         
37603         if (dm.multiSort) {
37604             // update the sortOrder
37605             var so = [];
37606             for(var i = 0; i < cm.config.length; i++ ) {
37607                 
37608                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37609                     continue; // dont' bother, it's not in sort list or being set.
37610                 }
37611                 
37612                 so.push(cm.config[i].dataIndex);
37613             };
37614             dm.sortOrder = so;
37615         }
37616         
37617         
37618         dm.sort(cm.getDataIndex(index));
37619     },
37620
37621
37622     destroy : function(){
37623         if(this.colMenu){
37624             this.colMenu.removeAll();
37625             Roo.menu.MenuMgr.unregister(this.colMenu);
37626             this.colMenu.getEl().remove();
37627             delete this.colMenu;
37628         }
37629         if(this.hmenu){
37630             this.hmenu.removeAll();
37631             Roo.menu.MenuMgr.unregister(this.hmenu);
37632             this.hmenu.getEl().remove();
37633             delete this.hmenu;
37634         }
37635         if(this.grid.enableColumnMove){
37636             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37637             if(dds){
37638                 for(var dd in dds){
37639                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37640                         var elid = dds[dd].dragElId;
37641                         dds[dd].unreg();
37642                         Roo.get(elid).remove();
37643                     } else if(dds[dd].config.isTarget){
37644                         dds[dd].proxyTop.remove();
37645                         dds[dd].proxyBottom.remove();
37646                         dds[dd].unreg();
37647                     }
37648                     if(Roo.dd.DDM.locationCache[dd]){
37649                         delete Roo.dd.DDM.locationCache[dd];
37650                     }
37651                 }
37652                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37653             }
37654         }
37655         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37656         this.bind(null, null);
37657         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37658     },
37659
37660     handleLockChange : function(){
37661         this.refresh(true);
37662     },
37663
37664     onDenyColumnLock : function(){
37665
37666     },
37667
37668     onDenyColumnHide : function(){
37669
37670     },
37671
37672     handleHdMenuClick : function(item){
37673         var index = this.hdCtxIndex;
37674         var cm = this.cm, ds = this.ds;
37675         switch(item.id){
37676             case "asc":
37677                 ds.sort(cm.getDataIndex(index), "ASC");
37678                 break;
37679             case "desc":
37680                 ds.sort(cm.getDataIndex(index), "DESC");
37681                 break;
37682             case "lock":
37683                 var lc = cm.getLockedCount();
37684                 if(cm.getColumnCount(true) <= lc+1){
37685                     this.onDenyColumnLock();
37686                     return;
37687                 }
37688                 if(lc != index){
37689                     cm.setLocked(index, true, true);
37690                     cm.moveColumn(index, lc);
37691                     this.grid.fireEvent("columnmove", index, lc);
37692                 }else{
37693                     cm.setLocked(index, true);
37694                 }
37695             break;
37696             case "unlock":
37697                 var lc = cm.getLockedCount();
37698                 if((lc-1) != index){
37699                     cm.setLocked(index, false, true);
37700                     cm.moveColumn(index, lc-1);
37701                     this.grid.fireEvent("columnmove", index, lc-1);
37702                 }else{
37703                     cm.setLocked(index, false);
37704                 }
37705             break;
37706             case 'wider': // used to expand cols on touch..
37707             case 'narrow':
37708                 var cw = cm.getColumnWidth(index);
37709                 cw += (item.id == 'wider' ? 1 : -1) * 50;
37710                 cw = Math.max(0, cw);
37711                 cw = Math.min(cw,4000);
37712                 cm.setColumnWidth(index, cw);
37713                 break;
37714                 
37715             default:
37716                 index = cm.getIndexById(item.id.substr(4));
37717                 if(index != -1){
37718                     if(item.checked && cm.getColumnCount(true) <= 1){
37719                         this.onDenyColumnHide();
37720                         return false;
37721                     }
37722                     cm.setHidden(index, item.checked);
37723                 }
37724         }
37725         return true;
37726     },
37727
37728     beforeColMenuShow : function(){
37729         var cm = this.cm,  colCount = cm.getColumnCount();
37730         this.colMenu.removeAll();
37731         for(var i = 0; i < colCount; i++){
37732             this.colMenu.add(new Roo.menu.CheckItem({
37733                 id: "col-"+cm.getColumnId(i),
37734                 text: cm.getColumnHeader(i),
37735                 checked: !cm.isHidden(i),
37736                 hideOnClick:false
37737             }));
37738         }
37739     },
37740
37741     handleHdCtx : function(g, index, e){
37742         e.stopEvent();
37743         var hd = this.getHeaderCell(index);
37744         this.hdCtxIndex = index;
37745         var ms = this.hmenu.items, cm = this.cm;
37746         ms.get("asc").setDisabled(!cm.isSortable(index));
37747         ms.get("desc").setDisabled(!cm.isSortable(index));
37748         if(this.grid.enableColLock !== false){
37749             ms.get("lock").setDisabled(cm.isLocked(index));
37750             ms.get("unlock").setDisabled(!cm.isLocked(index));
37751         }
37752         this.hmenu.show(hd, "tl-bl");
37753     },
37754
37755     handleHdOver : function(e){
37756         var hd = this.findHeaderCell(e.getTarget());
37757         if(hd && !this.headersDisabled){
37758             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37759                this.fly(hd).addClass("x-grid-hd-over");
37760             }
37761         }
37762     },
37763
37764     handleHdOut : function(e){
37765         var hd = this.findHeaderCell(e.getTarget());
37766         if(hd){
37767             this.fly(hd).removeClass("x-grid-hd-over");
37768         }
37769     },
37770
37771     handleSplitDblClick : function(e, t){
37772         var i = this.getCellIndex(t);
37773         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37774             this.autoSizeColumn(i, true);
37775             this.layout();
37776         }
37777     },
37778
37779     render : function(){
37780
37781         var cm = this.cm;
37782         var colCount = cm.getColumnCount();
37783
37784         if(this.grid.monitorWindowResize === true){
37785             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37786         }
37787         var header = this.renderHeaders();
37788         var body = this.templates.body.apply({rows:""});
37789         var html = this.templates.master.apply({
37790             lockedBody: body,
37791             body: body,
37792             lockedHeader: header[0],
37793             header: header[1]
37794         });
37795
37796         //this.updateColumns();
37797
37798         this.grid.getGridEl().dom.innerHTML = html;
37799
37800         this.initElements();
37801         
37802         // a kludge to fix the random scolling effect in webkit
37803         this.el.on("scroll", function() {
37804             this.el.dom.scrollTop=0; // hopefully not recursive..
37805         },this);
37806
37807         this.scroller.on("scroll", this.handleScroll, this);
37808         this.lockedBody.on("mousewheel", this.handleWheel, this);
37809         this.mainBody.on("mousewheel", this.handleWheel, this);
37810
37811         this.mainHd.on("mouseover", this.handleHdOver, this);
37812         this.mainHd.on("mouseout", this.handleHdOut, this);
37813         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37814                 {delegate: "."+this.splitClass});
37815
37816         this.lockedHd.on("mouseover", this.handleHdOver, this);
37817         this.lockedHd.on("mouseout", this.handleHdOut, this);
37818         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37819                 {delegate: "."+this.splitClass});
37820
37821         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37822             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37823         }
37824
37825         this.updateSplitters();
37826
37827         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37828             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37829             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37830         }
37831
37832         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37833             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37834             this.hmenu.add(
37835                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37836                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37837             );
37838             if(this.grid.enableColLock !== false){
37839                 this.hmenu.add('-',
37840                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37841                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37842                 );
37843             }
37844             if (Roo.isTouch) {
37845                  this.hmenu.add('-',
37846                     {id:"wider", text: this.columnsWiderText},
37847                     {id:"narrow", text: this.columnsNarrowText }
37848                 );
37849                 
37850                  
37851             }
37852             
37853             if(this.grid.enableColumnHide !== false){
37854
37855                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37856                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37857                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37858
37859                 this.hmenu.add('-',
37860                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37861                 );
37862             }
37863             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37864
37865             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37866         }
37867
37868         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37869             this.dd = new Roo.grid.GridDragZone(this.grid, {
37870                 ddGroup : this.grid.ddGroup || 'GridDD'
37871             });
37872             
37873         }
37874
37875         /*
37876         for(var i = 0; i < colCount; i++){
37877             if(cm.isHidden(i)){
37878                 this.hideColumn(i);
37879             }
37880             if(cm.config[i].align){
37881                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37882                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37883             }
37884         }*/
37885         
37886         this.updateHeaderSortState();
37887
37888         this.beforeInitialResize();
37889         this.layout(true);
37890
37891         // two part rendering gives faster view to the user
37892         this.renderPhase2.defer(1, this);
37893     },
37894
37895     renderPhase2 : function(){
37896         // render the rows now
37897         this.refresh();
37898         if(this.grid.autoSizeColumns){
37899             this.autoSizeColumns();
37900         }
37901     },
37902
37903     beforeInitialResize : function(){
37904
37905     },
37906
37907     onColumnSplitterMoved : function(i, w){
37908         this.userResized = true;
37909         var cm = this.grid.colModel;
37910         cm.setColumnWidth(i, w, true);
37911         var cid = cm.getColumnId(i);
37912         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37913         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37914         this.updateSplitters();
37915         this.layout();
37916         this.grid.fireEvent("columnresize", i, w);
37917     },
37918
37919     syncRowHeights : function(startIndex, endIndex){
37920         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37921             startIndex = startIndex || 0;
37922             var mrows = this.getBodyTable().rows;
37923             var lrows = this.getLockedTable().rows;
37924             var len = mrows.length-1;
37925             endIndex = Math.min(endIndex || len, len);
37926             for(var i = startIndex; i <= endIndex; i++){
37927                 var m = mrows[i], l = lrows[i];
37928                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37929                 m.style.height = l.style.height = h + "px";
37930             }
37931         }
37932     },
37933
37934     layout : function(initialRender, is2ndPass){
37935         var g = this.grid;
37936         var auto = g.autoHeight;
37937         var scrollOffset = 16;
37938         var c = g.getGridEl(), cm = this.cm,
37939                 expandCol = g.autoExpandColumn,
37940                 gv = this;
37941         //c.beginMeasure();
37942
37943         if(!c.dom.offsetWidth){ // display:none?
37944             if(initialRender){
37945                 this.lockedWrap.show();
37946                 this.mainWrap.show();
37947             }
37948             return;
37949         }
37950
37951         var hasLock = this.cm.isLocked(0);
37952
37953         var tbh = this.headerPanel.getHeight();
37954         var bbh = this.footerPanel.getHeight();
37955
37956         if(auto){
37957             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
37958             var newHeight = ch + c.getBorderWidth("tb");
37959             if(g.maxHeight){
37960                 newHeight = Math.min(g.maxHeight, newHeight);
37961             }
37962             c.setHeight(newHeight);
37963         }
37964
37965         if(g.autoWidth){
37966             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
37967         }
37968
37969         var s = this.scroller;
37970
37971         var csize = c.getSize(true);
37972
37973         this.el.setSize(csize.width, csize.height);
37974
37975         this.headerPanel.setWidth(csize.width);
37976         this.footerPanel.setWidth(csize.width);
37977
37978         var hdHeight = this.mainHd.getHeight();
37979         var vw = csize.width;
37980         var vh = csize.height - (tbh + bbh);
37981
37982         s.setSize(vw, vh);
37983
37984         var bt = this.getBodyTable();
37985         var ltWidth = hasLock ?
37986                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
37987
37988         var scrollHeight = bt.offsetHeight;
37989         var scrollWidth = ltWidth + bt.offsetWidth;
37990         var vscroll = false, hscroll = false;
37991
37992         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
37993
37994         var lw = this.lockedWrap, mw = this.mainWrap;
37995         var lb = this.lockedBody, mb = this.mainBody;
37996
37997         setTimeout(function(){
37998             var t = s.dom.offsetTop;
37999             var w = s.dom.clientWidth,
38000                 h = s.dom.clientHeight;
38001
38002             lw.setTop(t);
38003             lw.setSize(ltWidth, h);
38004
38005             mw.setLeftTop(ltWidth, t);
38006             mw.setSize(w-ltWidth, h);
38007
38008             lb.setHeight(h-hdHeight);
38009             mb.setHeight(h-hdHeight);
38010
38011             if(is2ndPass !== true && !gv.userResized && expandCol){
38012                 // high speed resize without full column calculation
38013                 
38014                 var ci = cm.getIndexById(expandCol);
38015                 if (ci < 0) {
38016                     ci = cm.findColumnIndex(expandCol);
38017                 }
38018                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38019                 var expandId = cm.getColumnId(ci);
38020                 var  tw = cm.getTotalWidth(false);
38021                 var currentWidth = cm.getColumnWidth(ci);
38022                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38023                 if(currentWidth != cw){
38024                     cm.setColumnWidth(ci, cw, true);
38025                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38026                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38027                     gv.updateSplitters();
38028                     gv.layout(false, true);
38029                 }
38030             }
38031
38032             if(initialRender){
38033                 lw.show();
38034                 mw.show();
38035             }
38036             //c.endMeasure();
38037         }, 10);
38038     },
38039
38040     onWindowResize : function(){
38041         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38042             return;
38043         }
38044         this.layout();
38045     },
38046
38047     appendFooter : function(parentEl){
38048         return null;
38049     },
38050
38051     sortAscText : "Sort Ascending",
38052     sortDescText : "Sort Descending",
38053     lockText : "Lock Column",
38054     unlockText : "Unlock Column",
38055     columnsText : "Columns",
38056  
38057     columnsWiderText : "Wider",
38058     columnsNarrowText : "Thinner"
38059 });
38060
38061
38062 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38063     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38064     this.proxy.el.addClass('x-grid3-col-dd');
38065 };
38066
38067 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38068     handleMouseDown : function(e){
38069
38070     },
38071
38072     callHandleMouseDown : function(e){
38073         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38074     }
38075 });
38076 /*
38077  * Based on:
38078  * Ext JS Library 1.1.1
38079  * Copyright(c) 2006-2007, Ext JS, LLC.
38080  *
38081  * Originally Released Under LGPL - original licence link has changed is not relivant.
38082  *
38083  * Fork - LGPL
38084  * <script type="text/javascript">
38085  */
38086  
38087 // private
38088 // This is a support class used internally by the Grid components
38089 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38090     this.grid = grid;
38091     this.view = grid.getView();
38092     this.proxy = this.view.resizeProxy;
38093     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38094         "gridSplitters" + this.grid.getGridEl().id, {
38095         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38096     });
38097     this.setHandleElId(Roo.id(hd));
38098     this.setOuterHandleElId(Roo.id(hd2));
38099     this.scroll = false;
38100 };
38101 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38102     fly: Roo.Element.fly,
38103
38104     b4StartDrag : function(x, y){
38105         this.view.headersDisabled = true;
38106         this.proxy.setHeight(this.view.mainWrap.getHeight());
38107         var w = this.cm.getColumnWidth(this.cellIndex);
38108         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38109         this.resetConstraints();
38110         this.setXConstraint(minw, 1000);
38111         this.setYConstraint(0, 0);
38112         this.minX = x - minw;
38113         this.maxX = x + 1000;
38114         this.startPos = x;
38115         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38116     },
38117
38118
38119     handleMouseDown : function(e){
38120         ev = Roo.EventObject.setEvent(e);
38121         var t = this.fly(ev.getTarget());
38122         if(t.hasClass("x-grid-split")){
38123             this.cellIndex = this.view.getCellIndex(t.dom);
38124             this.split = t.dom;
38125             this.cm = this.grid.colModel;
38126             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38127                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38128             }
38129         }
38130     },
38131
38132     endDrag : function(e){
38133         this.view.headersDisabled = false;
38134         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38135         var diff = endX - this.startPos;
38136         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38137     },
38138
38139     autoOffset : function(){
38140         this.setDelta(0,0);
38141     }
38142 });/*
38143  * Based on:
38144  * Ext JS Library 1.1.1
38145  * Copyright(c) 2006-2007, Ext JS, LLC.
38146  *
38147  * Originally Released Under LGPL - original licence link has changed is not relivant.
38148  *
38149  * Fork - LGPL
38150  * <script type="text/javascript">
38151  */
38152  
38153 // private
38154 // This is a support class used internally by the Grid components
38155 Roo.grid.GridDragZone = function(grid, config){
38156     this.view = grid.getView();
38157     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38158     if(this.view.lockedBody){
38159         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38160         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38161     }
38162     this.scroll = false;
38163     this.grid = grid;
38164     this.ddel = document.createElement('div');
38165     this.ddel.className = 'x-grid-dd-wrap';
38166 };
38167
38168 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38169     ddGroup : "GridDD",
38170
38171     getDragData : function(e){
38172         var t = Roo.lib.Event.getTarget(e);
38173         var rowIndex = this.view.findRowIndex(t);
38174         var sm = this.grid.selModel;
38175             
38176         //Roo.log(rowIndex);
38177         
38178         if (sm.getSelectedCell) {
38179             // cell selection..
38180             if (!sm.getSelectedCell()) {
38181                 return false;
38182             }
38183             if (rowIndex != sm.getSelectedCell()[0]) {
38184                 return false;
38185             }
38186         
38187         }
38188         
38189         if(rowIndex !== false){
38190             
38191             // if editorgrid.. 
38192             
38193             
38194             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38195                
38196             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38197               //  
38198             //}
38199             if (e.hasModifier()){
38200                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38201             }
38202             
38203             Roo.log("getDragData");
38204             
38205             return {
38206                 grid: this.grid,
38207                 ddel: this.ddel,
38208                 rowIndex: rowIndex,
38209                 selections:sm.getSelections ? sm.getSelections() : (
38210                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38211                 )
38212             };
38213         }
38214         return false;
38215     },
38216
38217     onInitDrag : function(e){
38218         var data = this.dragData;
38219         this.ddel.innerHTML = this.grid.getDragDropText();
38220         this.proxy.update(this.ddel);
38221         // fire start drag?
38222     },
38223
38224     afterRepair : function(){
38225         this.dragging = false;
38226     },
38227
38228     getRepairXY : function(e, data){
38229         return false;
38230     },
38231
38232     onEndDrag : function(data, e){
38233         // fire end drag?
38234     },
38235
38236     onValidDrop : function(dd, e, id){
38237         // fire drag drop?
38238         this.hideProxy();
38239     },
38240
38241     beforeInvalidDrop : function(e, id){
38242
38243     }
38244 });/*
38245  * Based on:
38246  * Ext JS Library 1.1.1
38247  * Copyright(c) 2006-2007, Ext JS, LLC.
38248  *
38249  * Originally Released Under LGPL - original licence link has changed is not relivant.
38250  *
38251  * Fork - LGPL
38252  * <script type="text/javascript">
38253  */
38254  
38255
38256 /**
38257  * @class Roo.grid.ColumnModel
38258  * @extends Roo.util.Observable
38259  * This is the default implementation of a ColumnModel used by the Grid. It defines
38260  * the columns in the grid.
38261  * <br>Usage:<br>
38262  <pre><code>
38263  var colModel = new Roo.grid.ColumnModel([
38264         {header: "Ticker", width: 60, sortable: true, locked: true},
38265         {header: "Company Name", width: 150, sortable: true},
38266         {header: "Market Cap.", width: 100, sortable: true},
38267         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38268         {header: "Employees", width: 100, sortable: true, resizable: false}
38269  ]);
38270  </code></pre>
38271  * <p>
38272  
38273  * The config options listed for this class are options which may appear in each
38274  * individual column definition.
38275  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38276  * @constructor
38277  * @param {Object} config An Array of column config objects. See this class's
38278  * config objects for details.
38279 */
38280 Roo.grid.ColumnModel = function(config){
38281         /**
38282      * The config passed into the constructor
38283      */
38284     this.config = config;
38285     this.lookup = {};
38286
38287     // if no id, create one
38288     // if the column does not have a dataIndex mapping,
38289     // map it to the order it is in the config
38290     for(var i = 0, len = config.length; i < len; i++){
38291         var c = config[i];
38292         if(typeof c.dataIndex == "undefined"){
38293             c.dataIndex = i;
38294         }
38295         if(typeof c.renderer == "string"){
38296             c.renderer = Roo.util.Format[c.renderer];
38297         }
38298         if(typeof c.id == "undefined"){
38299             c.id = Roo.id();
38300         }
38301         if(c.editor && c.editor.xtype){
38302             c.editor  = Roo.factory(c.editor, Roo.grid);
38303         }
38304         if(c.editor && c.editor.isFormField){
38305             c.editor = new Roo.grid.GridEditor(c.editor);
38306         }
38307         this.lookup[c.id] = c;
38308     }
38309
38310     /**
38311      * The width of columns which have no width specified (defaults to 100)
38312      * @type Number
38313      */
38314     this.defaultWidth = 100;
38315
38316     /**
38317      * Default sortable of columns which have no sortable specified (defaults to false)
38318      * @type Boolean
38319      */
38320     this.defaultSortable = false;
38321
38322     this.addEvents({
38323         /**
38324              * @event widthchange
38325              * Fires when the width of a column changes.
38326              * @param {ColumnModel} this
38327              * @param {Number} columnIndex The column index
38328              * @param {Number} newWidth The new width
38329              */
38330             "widthchange": true,
38331         /**
38332              * @event headerchange
38333              * Fires when the text of a header changes.
38334              * @param {ColumnModel} this
38335              * @param {Number} columnIndex The column index
38336              * @param {Number} newText The new header text
38337              */
38338             "headerchange": true,
38339         /**
38340              * @event hiddenchange
38341              * Fires when a column is hidden or "unhidden".
38342              * @param {ColumnModel} this
38343              * @param {Number} columnIndex The column index
38344              * @param {Boolean} hidden true if hidden, false otherwise
38345              */
38346             "hiddenchange": true,
38347             /**
38348          * @event columnmoved
38349          * Fires when a column is moved.
38350          * @param {ColumnModel} this
38351          * @param {Number} oldIndex
38352          * @param {Number} newIndex
38353          */
38354         "columnmoved" : true,
38355         /**
38356          * @event columlockchange
38357          * Fires when a column's locked state is changed
38358          * @param {ColumnModel} this
38359          * @param {Number} colIndex
38360          * @param {Boolean} locked true if locked
38361          */
38362         "columnlockchange" : true
38363     });
38364     Roo.grid.ColumnModel.superclass.constructor.call(this);
38365 };
38366 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38367     /**
38368      * @cfg {String} header The header text to display in the Grid view.
38369      */
38370     /**
38371      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38372      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38373      * specified, the column's index is used as an index into the Record's data Array.
38374      */
38375     /**
38376      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38377      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38378      */
38379     /**
38380      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38381      * Defaults to the value of the {@link #defaultSortable} property.
38382      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38383      */
38384     /**
38385      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38386      */
38387     /**
38388      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38389      */
38390     /**
38391      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38392      */
38393     /**
38394      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38395      */
38396     /**
38397      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38398      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38399      * default renderer uses the raw data value.
38400      */
38401        /**
38402      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38403      */
38404     /**
38405      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38406      */
38407
38408     /**
38409      * Returns the id of the column at the specified index.
38410      * @param {Number} index The column index
38411      * @return {String} the id
38412      */
38413     getColumnId : function(index){
38414         return this.config[index].id;
38415     },
38416
38417     /**
38418      * Returns the column for a specified id.
38419      * @param {String} id The column id
38420      * @return {Object} the column
38421      */
38422     getColumnById : function(id){
38423         return this.lookup[id];
38424     },
38425
38426     
38427     /**
38428      * Returns the column for a specified dataIndex.
38429      * @param {String} dataIndex The column dataIndex
38430      * @return {Object|Boolean} the column or false if not found
38431      */
38432     getColumnByDataIndex: function(dataIndex){
38433         var index = this.findColumnIndex(dataIndex);
38434         return index > -1 ? this.config[index] : false;
38435     },
38436     
38437     /**
38438      * Returns the index for a specified column id.
38439      * @param {String} id The column id
38440      * @return {Number} the index, or -1 if not found
38441      */
38442     getIndexById : function(id){
38443         for(var i = 0, len = this.config.length; i < len; i++){
38444             if(this.config[i].id == id){
38445                 return i;
38446             }
38447         }
38448         return -1;
38449     },
38450     
38451     /**
38452      * Returns the index for a specified column dataIndex.
38453      * @param {String} dataIndex The column dataIndex
38454      * @return {Number} the index, or -1 if not found
38455      */
38456     
38457     findColumnIndex : function(dataIndex){
38458         for(var i = 0, len = this.config.length; i < len; i++){
38459             if(this.config[i].dataIndex == dataIndex){
38460                 return i;
38461             }
38462         }
38463         return -1;
38464     },
38465     
38466     
38467     moveColumn : function(oldIndex, newIndex){
38468         var c = this.config[oldIndex];
38469         this.config.splice(oldIndex, 1);
38470         this.config.splice(newIndex, 0, c);
38471         this.dataMap = null;
38472         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38473     },
38474
38475     isLocked : function(colIndex){
38476         return this.config[colIndex].locked === true;
38477     },
38478
38479     setLocked : function(colIndex, value, suppressEvent){
38480         if(this.isLocked(colIndex) == value){
38481             return;
38482         }
38483         this.config[colIndex].locked = value;
38484         if(!suppressEvent){
38485             this.fireEvent("columnlockchange", this, colIndex, value);
38486         }
38487     },
38488
38489     getTotalLockedWidth : function(){
38490         var totalWidth = 0;
38491         for(var i = 0; i < this.config.length; i++){
38492             if(this.isLocked(i) && !this.isHidden(i)){
38493                 this.totalWidth += this.getColumnWidth(i);
38494             }
38495         }
38496         return totalWidth;
38497     },
38498
38499     getLockedCount : function(){
38500         for(var i = 0, len = this.config.length; i < len; i++){
38501             if(!this.isLocked(i)){
38502                 return i;
38503             }
38504         }
38505     },
38506
38507     /**
38508      * Returns the number of columns.
38509      * @return {Number}
38510      */
38511     getColumnCount : function(visibleOnly){
38512         if(visibleOnly === true){
38513             var c = 0;
38514             for(var i = 0, len = this.config.length; i < len; i++){
38515                 if(!this.isHidden(i)){
38516                     c++;
38517                 }
38518             }
38519             return c;
38520         }
38521         return this.config.length;
38522     },
38523
38524     /**
38525      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38526      * @param {Function} fn
38527      * @param {Object} scope (optional)
38528      * @return {Array} result
38529      */
38530     getColumnsBy : function(fn, scope){
38531         var r = [];
38532         for(var i = 0, len = this.config.length; i < len; i++){
38533             var c = this.config[i];
38534             if(fn.call(scope||this, c, i) === true){
38535                 r[r.length] = c;
38536             }
38537         }
38538         return r;
38539     },
38540
38541     /**
38542      * Returns true if the specified column is sortable.
38543      * @param {Number} col The column index
38544      * @return {Boolean}
38545      */
38546     isSortable : function(col){
38547         if(typeof this.config[col].sortable == "undefined"){
38548             return this.defaultSortable;
38549         }
38550         return this.config[col].sortable;
38551     },
38552
38553     /**
38554      * Returns the rendering (formatting) function defined for the column.
38555      * @param {Number} col The column index.
38556      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38557      */
38558     getRenderer : function(col){
38559         if(!this.config[col].renderer){
38560             return Roo.grid.ColumnModel.defaultRenderer;
38561         }
38562         return this.config[col].renderer;
38563     },
38564
38565     /**
38566      * Sets the rendering (formatting) function for a column.
38567      * @param {Number} col The column index
38568      * @param {Function} fn The function to use to process the cell's raw data
38569      * to return HTML markup for the grid view. The render function is called with
38570      * the following parameters:<ul>
38571      * <li>Data value.</li>
38572      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38573      * <li>css A CSS style string to apply to the table cell.</li>
38574      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38575      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38576      * <li>Row index</li>
38577      * <li>Column index</li>
38578      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38579      */
38580     setRenderer : function(col, fn){
38581         this.config[col].renderer = fn;
38582     },
38583
38584     /**
38585      * Returns the width for the specified column.
38586      * @param {Number} col The column index
38587      * @return {Number}
38588      */
38589     getColumnWidth : function(col){
38590         return this.config[col].width * 1 || this.defaultWidth;
38591     },
38592
38593     /**
38594      * Sets the width for a column.
38595      * @param {Number} col The column index
38596      * @param {Number} width The new width
38597      */
38598     setColumnWidth : function(col, width, suppressEvent){
38599         this.config[col].width = width;
38600         this.totalWidth = null;
38601         if(!suppressEvent){
38602              this.fireEvent("widthchange", this, col, width);
38603         }
38604     },
38605
38606     /**
38607      * Returns the total width of all columns.
38608      * @param {Boolean} includeHidden True to include hidden column widths
38609      * @return {Number}
38610      */
38611     getTotalWidth : function(includeHidden){
38612         if(!this.totalWidth){
38613             this.totalWidth = 0;
38614             for(var i = 0, len = this.config.length; i < len; i++){
38615                 if(includeHidden || !this.isHidden(i)){
38616                     this.totalWidth += this.getColumnWidth(i);
38617                 }
38618             }
38619         }
38620         return this.totalWidth;
38621     },
38622
38623     /**
38624      * Returns the header for the specified column.
38625      * @param {Number} col The column index
38626      * @return {String}
38627      */
38628     getColumnHeader : function(col){
38629         return this.config[col].header;
38630     },
38631
38632     /**
38633      * Sets the header for a column.
38634      * @param {Number} col The column index
38635      * @param {String} header The new header
38636      */
38637     setColumnHeader : function(col, header){
38638         this.config[col].header = header;
38639         this.fireEvent("headerchange", this, col, header);
38640     },
38641
38642     /**
38643      * Returns the tooltip for the specified column.
38644      * @param {Number} col The column index
38645      * @return {String}
38646      */
38647     getColumnTooltip : function(col){
38648             return this.config[col].tooltip;
38649     },
38650     /**
38651      * Sets the tooltip for a column.
38652      * @param {Number} col The column index
38653      * @param {String} tooltip The new tooltip
38654      */
38655     setColumnTooltip : function(col, tooltip){
38656             this.config[col].tooltip = tooltip;
38657     },
38658
38659     /**
38660      * Returns the dataIndex for the specified column.
38661      * @param {Number} col The column index
38662      * @return {Number}
38663      */
38664     getDataIndex : function(col){
38665         return this.config[col].dataIndex;
38666     },
38667
38668     /**
38669      * Sets the dataIndex for a column.
38670      * @param {Number} col The column index
38671      * @param {Number} dataIndex The new dataIndex
38672      */
38673     setDataIndex : function(col, dataIndex){
38674         this.config[col].dataIndex = dataIndex;
38675     },
38676
38677     
38678     
38679     /**
38680      * Returns true if the cell is editable.
38681      * @param {Number} colIndex The column index
38682      * @param {Number} rowIndex The row index
38683      * @return {Boolean}
38684      */
38685     isCellEditable : function(colIndex, rowIndex){
38686         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38687     },
38688
38689     /**
38690      * Returns the editor defined for the cell/column.
38691      * return false or null to disable editing.
38692      * @param {Number} colIndex The column index
38693      * @param {Number} rowIndex The row index
38694      * @return {Object}
38695      */
38696     getCellEditor : function(colIndex, rowIndex){
38697         return this.config[colIndex].editor;
38698     },
38699
38700     /**
38701      * Sets if a column is editable.
38702      * @param {Number} col The column index
38703      * @param {Boolean} editable True if the column is editable
38704      */
38705     setEditable : function(col, editable){
38706         this.config[col].editable = editable;
38707     },
38708
38709
38710     /**
38711      * Returns true if the column is hidden.
38712      * @param {Number} colIndex The column index
38713      * @return {Boolean}
38714      */
38715     isHidden : function(colIndex){
38716         return this.config[colIndex].hidden;
38717     },
38718
38719
38720     /**
38721      * Returns true if the column width cannot be changed
38722      */
38723     isFixed : function(colIndex){
38724         return this.config[colIndex].fixed;
38725     },
38726
38727     /**
38728      * Returns true if the column can be resized
38729      * @return {Boolean}
38730      */
38731     isResizable : function(colIndex){
38732         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38733     },
38734     /**
38735      * Sets if a column is hidden.
38736      * @param {Number} colIndex The column index
38737      * @param {Boolean} hidden True if the column is hidden
38738      */
38739     setHidden : function(colIndex, hidden){
38740         this.config[colIndex].hidden = hidden;
38741         this.totalWidth = null;
38742         this.fireEvent("hiddenchange", this, colIndex, hidden);
38743     },
38744
38745     /**
38746      * Sets the editor for a column.
38747      * @param {Number} col The column index
38748      * @param {Object} editor The editor object
38749      */
38750     setEditor : function(col, editor){
38751         this.config[col].editor = editor;
38752     }
38753 });
38754
38755 Roo.grid.ColumnModel.defaultRenderer = function(value){
38756         if(typeof value == "string" && value.length < 1){
38757             return "&#160;";
38758         }
38759         return value;
38760 };
38761
38762 // Alias for backwards compatibility
38763 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38764 /*
38765  * Based on:
38766  * Ext JS Library 1.1.1
38767  * Copyright(c) 2006-2007, Ext JS, LLC.
38768  *
38769  * Originally Released Under LGPL - original licence link has changed is not relivant.
38770  *
38771  * Fork - LGPL
38772  * <script type="text/javascript">
38773  */
38774
38775 /**
38776  * @class Roo.grid.AbstractSelectionModel
38777  * @extends Roo.util.Observable
38778  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38779  * implemented by descendant classes.  This class should not be directly instantiated.
38780  * @constructor
38781  */
38782 Roo.grid.AbstractSelectionModel = function(){
38783     this.locked = false;
38784     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38785 };
38786
38787 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38788     /** @ignore Called by the grid automatically. Do not call directly. */
38789     init : function(grid){
38790         this.grid = grid;
38791         this.initEvents();
38792     },
38793
38794     /**
38795      * Locks the selections.
38796      */
38797     lock : function(){
38798         this.locked = true;
38799     },
38800
38801     /**
38802      * Unlocks the selections.
38803      */
38804     unlock : function(){
38805         this.locked = false;
38806     },
38807
38808     /**
38809      * Returns true if the selections are locked.
38810      * @return {Boolean}
38811      */
38812     isLocked : function(){
38813         return this.locked;
38814     }
38815 });/*
38816  * Based on:
38817  * Ext JS Library 1.1.1
38818  * Copyright(c) 2006-2007, Ext JS, LLC.
38819  *
38820  * Originally Released Under LGPL - original licence link has changed is not relivant.
38821  *
38822  * Fork - LGPL
38823  * <script type="text/javascript">
38824  */
38825 /**
38826  * @extends Roo.grid.AbstractSelectionModel
38827  * @class Roo.grid.RowSelectionModel
38828  * The default SelectionModel used by {@link Roo.grid.Grid}.
38829  * It supports multiple selections and keyboard selection/navigation. 
38830  * @constructor
38831  * @param {Object} config
38832  */
38833 Roo.grid.RowSelectionModel = function(config){
38834     Roo.apply(this, config);
38835     this.selections = new Roo.util.MixedCollection(false, function(o){
38836         return o.id;
38837     });
38838
38839     this.last = false;
38840     this.lastActive = false;
38841
38842     this.addEvents({
38843         /**
38844              * @event selectionchange
38845              * Fires when the selection changes
38846              * @param {SelectionModel} this
38847              */
38848             "selectionchange" : true,
38849         /**
38850              * @event afterselectionchange
38851              * Fires after the selection changes (eg. by key press or clicking)
38852              * @param {SelectionModel} this
38853              */
38854             "afterselectionchange" : true,
38855         /**
38856              * @event beforerowselect
38857              * Fires when a row is selected being selected, return false to cancel.
38858              * @param {SelectionModel} this
38859              * @param {Number} rowIndex The selected index
38860              * @param {Boolean} keepExisting False if other selections will be cleared
38861              */
38862             "beforerowselect" : true,
38863         /**
38864              * @event rowselect
38865              * Fires when a row is selected.
38866              * @param {SelectionModel} this
38867              * @param {Number} rowIndex The selected index
38868              * @param {Roo.data.Record} r The record
38869              */
38870             "rowselect" : true,
38871         /**
38872              * @event rowdeselect
38873              * Fires when a row is deselected.
38874              * @param {SelectionModel} this
38875              * @param {Number} rowIndex The selected index
38876              */
38877         "rowdeselect" : true
38878     });
38879     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38880     this.locked = false;
38881 };
38882
38883 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38884     /**
38885      * @cfg {Boolean} singleSelect
38886      * True to allow selection of only one row at a time (defaults to false)
38887      */
38888     singleSelect : false,
38889
38890     // private
38891     initEvents : function(){
38892
38893         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38894             this.grid.on("mousedown", this.handleMouseDown, this);
38895         }else{ // allow click to work like normal
38896             this.grid.on("rowclick", this.handleDragableRowClick, this);
38897         }
38898
38899         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38900             "up" : function(e){
38901                 if(!e.shiftKey){
38902                     this.selectPrevious(e.shiftKey);
38903                 }else if(this.last !== false && this.lastActive !== false){
38904                     var last = this.last;
38905                     this.selectRange(this.last,  this.lastActive-1);
38906                     this.grid.getView().focusRow(this.lastActive);
38907                     if(last !== false){
38908                         this.last = last;
38909                     }
38910                 }else{
38911                     this.selectFirstRow();
38912                 }
38913                 this.fireEvent("afterselectionchange", this);
38914             },
38915             "down" : function(e){
38916                 if(!e.shiftKey){
38917                     this.selectNext(e.shiftKey);
38918                 }else if(this.last !== false && this.lastActive !== false){
38919                     var last = this.last;
38920                     this.selectRange(this.last,  this.lastActive+1);
38921                     this.grid.getView().focusRow(this.lastActive);
38922                     if(last !== false){
38923                         this.last = last;
38924                     }
38925                 }else{
38926                     this.selectFirstRow();
38927                 }
38928                 this.fireEvent("afterselectionchange", this);
38929             },
38930             scope: this
38931         });
38932
38933         var view = this.grid.view;
38934         view.on("refresh", this.onRefresh, this);
38935         view.on("rowupdated", this.onRowUpdated, this);
38936         view.on("rowremoved", this.onRemove, this);
38937     },
38938
38939     // private
38940     onRefresh : function(){
38941         var ds = this.grid.dataSource, i, v = this.grid.view;
38942         var s = this.selections;
38943         s.each(function(r){
38944             if((i = ds.indexOfId(r.id)) != -1){
38945                 v.onRowSelect(i);
38946             }else{
38947                 s.remove(r);
38948             }
38949         });
38950     },
38951
38952     // private
38953     onRemove : function(v, index, r){
38954         this.selections.remove(r);
38955     },
38956
38957     // private
38958     onRowUpdated : function(v, index, r){
38959         if(this.isSelected(r)){
38960             v.onRowSelect(index);
38961         }
38962     },
38963
38964     /**
38965      * Select records.
38966      * @param {Array} records The records to select
38967      * @param {Boolean} keepExisting (optional) True to keep existing selections
38968      */
38969     selectRecords : function(records, keepExisting){
38970         if(!keepExisting){
38971             this.clearSelections();
38972         }
38973         var ds = this.grid.dataSource;
38974         for(var i = 0, len = records.length; i < len; i++){
38975             this.selectRow(ds.indexOf(records[i]), true);
38976         }
38977     },
38978
38979     /**
38980      * Gets the number of selected rows.
38981      * @return {Number}
38982      */
38983     getCount : function(){
38984         return this.selections.length;
38985     },
38986
38987     /**
38988      * Selects the first row in the grid.
38989      */
38990     selectFirstRow : function(){
38991         this.selectRow(0);
38992     },
38993
38994     /**
38995      * Select the last row.
38996      * @param {Boolean} keepExisting (optional) True to keep existing selections
38997      */
38998     selectLastRow : function(keepExisting){
38999         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39000     },
39001
39002     /**
39003      * Selects the row immediately following the last selected row.
39004      * @param {Boolean} keepExisting (optional) True to keep existing selections
39005      */
39006     selectNext : function(keepExisting){
39007         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39008             this.selectRow(this.last+1, keepExisting);
39009             this.grid.getView().focusRow(this.last);
39010         }
39011     },
39012
39013     /**
39014      * Selects the row that precedes the last selected row.
39015      * @param {Boolean} keepExisting (optional) True to keep existing selections
39016      */
39017     selectPrevious : function(keepExisting){
39018         if(this.last){
39019             this.selectRow(this.last-1, keepExisting);
39020             this.grid.getView().focusRow(this.last);
39021         }
39022     },
39023
39024     /**
39025      * Returns the selected records
39026      * @return {Array} Array of selected records
39027      */
39028     getSelections : function(){
39029         return [].concat(this.selections.items);
39030     },
39031
39032     /**
39033      * Returns the first selected record.
39034      * @return {Record}
39035      */
39036     getSelected : function(){
39037         return this.selections.itemAt(0);
39038     },
39039
39040
39041     /**
39042      * Clears all selections.
39043      */
39044     clearSelections : function(fast){
39045         if(this.locked) return;
39046         if(fast !== true){
39047             var ds = this.grid.dataSource;
39048             var s = this.selections;
39049             s.each(function(r){
39050                 this.deselectRow(ds.indexOfId(r.id));
39051             }, this);
39052             s.clear();
39053         }else{
39054             this.selections.clear();
39055         }
39056         this.last = false;
39057     },
39058
39059
39060     /**
39061      * Selects all rows.
39062      */
39063     selectAll : function(){
39064         if(this.locked) return;
39065         this.selections.clear();
39066         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39067             this.selectRow(i, true);
39068         }
39069     },
39070
39071     /**
39072      * Returns True if there is a selection.
39073      * @return {Boolean}
39074      */
39075     hasSelection : function(){
39076         return this.selections.length > 0;
39077     },
39078
39079     /**
39080      * Returns True if the specified row is selected.
39081      * @param {Number/Record} record The record or index of the record to check
39082      * @return {Boolean}
39083      */
39084     isSelected : function(index){
39085         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39086         return (r && this.selections.key(r.id) ? true : false);
39087     },
39088
39089     /**
39090      * Returns True if the specified record id is selected.
39091      * @param {String} id The id of record to check
39092      * @return {Boolean}
39093      */
39094     isIdSelected : function(id){
39095         return (this.selections.key(id) ? true : false);
39096     },
39097
39098     // private
39099     handleMouseDown : function(e, t){
39100         var view = this.grid.getView(), rowIndex;
39101         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39102             return;
39103         };
39104         if(e.shiftKey && this.last !== false){
39105             var last = this.last;
39106             this.selectRange(last, rowIndex, e.ctrlKey);
39107             this.last = last; // reset the last
39108             view.focusRow(rowIndex);
39109         }else{
39110             var isSelected = this.isSelected(rowIndex);
39111             if(e.button !== 0 && isSelected){
39112                 view.focusRow(rowIndex);
39113             }else if(e.ctrlKey && isSelected){
39114                 this.deselectRow(rowIndex);
39115             }else if(!isSelected){
39116                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39117                 view.focusRow(rowIndex);
39118             }
39119         }
39120         this.fireEvent("afterselectionchange", this);
39121     },
39122     // private
39123     handleDragableRowClick :  function(grid, rowIndex, e) 
39124     {
39125         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39126             this.selectRow(rowIndex, false);
39127             grid.view.focusRow(rowIndex);
39128              this.fireEvent("afterselectionchange", this);
39129         }
39130     },
39131     
39132     /**
39133      * Selects multiple rows.
39134      * @param {Array} rows Array of the indexes of the row to select
39135      * @param {Boolean} keepExisting (optional) True to keep existing selections
39136      */
39137     selectRows : function(rows, keepExisting){
39138         if(!keepExisting){
39139             this.clearSelections();
39140         }
39141         for(var i = 0, len = rows.length; i < len; i++){
39142             this.selectRow(rows[i], true);
39143         }
39144     },
39145
39146     /**
39147      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39148      * @param {Number} startRow The index of the first row in the range
39149      * @param {Number} endRow The index of the last row in the range
39150      * @param {Boolean} keepExisting (optional) True to retain existing selections
39151      */
39152     selectRange : function(startRow, endRow, keepExisting){
39153         if(this.locked) return;
39154         if(!keepExisting){
39155             this.clearSelections();
39156         }
39157         if(startRow <= endRow){
39158             for(var i = startRow; i <= endRow; i++){
39159                 this.selectRow(i, true);
39160             }
39161         }else{
39162             for(var i = startRow; i >= endRow; i--){
39163                 this.selectRow(i, true);
39164             }
39165         }
39166     },
39167
39168     /**
39169      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39170      * @param {Number} startRow The index of the first row in the range
39171      * @param {Number} endRow The index of the last row in the range
39172      */
39173     deselectRange : function(startRow, endRow, preventViewNotify){
39174         if(this.locked) return;
39175         for(var i = startRow; i <= endRow; i++){
39176             this.deselectRow(i, preventViewNotify);
39177         }
39178     },
39179
39180     /**
39181      * Selects a row.
39182      * @param {Number} row The index of the row to select
39183      * @param {Boolean} keepExisting (optional) True to keep existing selections
39184      */
39185     selectRow : function(index, keepExisting, preventViewNotify){
39186         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39187         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39188             if(!keepExisting || this.singleSelect){
39189                 this.clearSelections();
39190             }
39191             var r = this.grid.dataSource.getAt(index);
39192             this.selections.add(r);
39193             this.last = this.lastActive = index;
39194             if(!preventViewNotify){
39195                 this.grid.getView().onRowSelect(index);
39196             }
39197             this.fireEvent("rowselect", this, index, r);
39198             this.fireEvent("selectionchange", this);
39199         }
39200     },
39201
39202     /**
39203      * Deselects a row.
39204      * @param {Number} row The index of the row to deselect
39205      */
39206     deselectRow : function(index, preventViewNotify){
39207         if(this.locked) return;
39208         if(this.last == index){
39209             this.last = false;
39210         }
39211         if(this.lastActive == index){
39212             this.lastActive = false;
39213         }
39214         var r = this.grid.dataSource.getAt(index);
39215         this.selections.remove(r);
39216         if(!preventViewNotify){
39217             this.grid.getView().onRowDeselect(index);
39218         }
39219         this.fireEvent("rowdeselect", this, index);
39220         this.fireEvent("selectionchange", this);
39221     },
39222
39223     // private
39224     restoreLast : function(){
39225         if(this._last){
39226             this.last = this._last;
39227         }
39228     },
39229
39230     // private
39231     acceptsNav : function(row, col, cm){
39232         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39233     },
39234
39235     // private
39236     onEditorKey : function(field, e){
39237         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39238         if(k == e.TAB){
39239             e.stopEvent();
39240             ed.completeEdit();
39241             if(e.shiftKey){
39242                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39243             }else{
39244                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39245             }
39246         }else if(k == e.ENTER && !e.ctrlKey){
39247             e.stopEvent();
39248             ed.completeEdit();
39249             if(e.shiftKey){
39250                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39251             }else{
39252                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39253             }
39254         }else if(k == e.ESC){
39255             ed.cancelEdit();
39256         }
39257         if(newCell){
39258             g.startEditing(newCell[0], newCell[1]);
39259         }
39260     }
39261 });/*
39262  * Based on:
39263  * Ext JS Library 1.1.1
39264  * Copyright(c) 2006-2007, Ext JS, LLC.
39265  *
39266  * Originally Released Under LGPL - original licence link has changed is not relivant.
39267  *
39268  * Fork - LGPL
39269  * <script type="text/javascript">
39270  */
39271 /**
39272  * @class Roo.grid.CellSelectionModel
39273  * @extends Roo.grid.AbstractSelectionModel
39274  * This class provides the basic implementation for cell selection in a grid.
39275  * @constructor
39276  * @param {Object} config The object containing the configuration of this model.
39277  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39278  */
39279 Roo.grid.CellSelectionModel = function(config){
39280     Roo.apply(this, config);
39281
39282     this.selection = null;
39283
39284     this.addEvents({
39285         /**
39286              * @event beforerowselect
39287              * Fires before a cell is selected.
39288              * @param {SelectionModel} this
39289              * @param {Number} rowIndex The selected row index
39290              * @param {Number} colIndex The selected cell index
39291              */
39292             "beforecellselect" : true,
39293         /**
39294              * @event cellselect
39295              * Fires when a cell is selected.
39296              * @param {SelectionModel} this
39297              * @param {Number} rowIndex The selected row index
39298              * @param {Number} colIndex The selected cell index
39299              */
39300             "cellselect" : true,
39301         /**
39302              * @event selectionchange
39303              * Fires when the active selection changes.
39304              * @param {SelectionModel} this
39305              * @param {Object} selection null for no selection or an object (o) with two properties
39306                 <ul>
39307                 <li>o.record: the record object for the row the selection is in</li>
39308                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39309                 </ul>
39310              */
39311             "selectionchange" : true,
39312         /**
39313              * @event tabend
39314              * Fires when the tab (or enter) was pressed on the last editable cell
39315              * You can use this to trigger add new row.
39316              * @param {SelectionModel} this
39317              */
39318             "tabend" : true,
39319          /**
39320              * @event beforeeditnext
39321              * Fires before the next editable sell is made active
39322              * You can use this to skip to another cell or fire the tabend
39323              *    if you set cell to false
39324              * @param {Object} eventdata object : { cell : [ row, col ] } 
39325              */
39326             "beforeeditnext" : true
39327     });
39328     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39329 };
39330
39331 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39332     
39333     enter_is_tab: false,
39334
39335     /** @ignore */
39336     initEvents : function(){
39337         this.grid.on("mousedown", this.handleMouseDown, this);
39338         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39339         var view = this.grid.view;
39340         view.on("refresh", this.onViewChange, this);
39341         view.on("rowupdated", this.onRowUpdated, this);
39342         view.on("beforerowremoved", this.clearSelections, this);
39343         view.on("beforerowsinserted", this.clearSelections, this);
39344         if(this.grid.isEditor){
39345             this.grid.on("beforeedit", this.beforeEdit,  this);
39346         }
39347     },
39348
39349         //private
39350     beforeEdit : function(e){
39351         this.select(e.row, e.column, false, true, e.record);
39352     },
39353
39354         //private
39355     onRowUpdated : function(v, index, r){
39356         if(this.selection && this.selection.record == r){
39357             v.onCellSelect(index, this.selection.cell[1]);
39358         }
39359     },
39360
39361         //private
39362     onViewChange : function(){
39363         this.clearSelections(true);
39364     },
39365
39366         /**
39367          * Returns the currently selected cell,.
39368          * @return {Array} The selected cell (row, column) or null if none selected.
39369          */
39370     getSelectedCell : function(){
39371         return this.selection ? this.selection.cell : null;
39372     },
39373
39374     /**
39375      * Clears all selections.
39376      * @param {Boolean} true to prevent the gridview from being notified about the change.
39377      */
39378     clearSelections : function(preventNotify){
39379         var s = this.selection;
39380         if(s){
39381             if(preventNotify !== true){
39382                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39383             }
39384             this.selection = null;
39385             this.fireEvent("selectionchange", this, null);
39386         }
39387     },
39388
39389     /**
39390      * Returns true if there is a selection.
39391      * @return {Boolean}
39392      */
39393     hasSelection : function(){
39394         return this.selection ? true : false;
39395     },
39396
39397     /** @ignore */
39398     handleMouseDown : function(e, t){
39399         var v = this.grid.getView();
39400         if(this.isLocked()){
39401             return;
39402         };
39403         var row = v.findRowIndex(t);
39404         var cell = v.findCellIndex(t);
39405         if(row !== false && cell !== false){
39406             this.select(row, cell);
39407         }
39408     },
39409
39410     /**
39411      * Selects a cell.
39412      * @param {Number} rowIndex
39413      * @param {Number} collIndex
39414      */
39415     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39416         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39417             this.clearSelections();
39418             r = r || this.grid.dataSource.getAt(rowIndex);
39419             this.selection = {
39420                 record : r,
39421                 cell : [rowIndex, colIndex]
39422             };
39423             if(!preventViewNotify){
39424                 var v = this.grid.getView();
39425                 v.onCellSelect(rowIndex, colIndex);
39426                 if(preventFocus !== true){
39427                     v.focusCell(rowIndex, colIndex);
39428                 }
39429             }
39430             this.fireEvent("cellselect", this, rowIndex, colIndex);
39431             this.fireEvent("selectionchange", this, this.selection);
39432         }
39433     },
39434
39435         //private
39436     isSelectable : function(rowIndex, colIndex, cm){
39437         return !cm.isHidden(colIndex);
39438     },
39439
39440     /** @ignore */
39441     handleKeyDown : function(e){
39442         //Roo.log('Cell Sel Model handleKeyDown');
39443         if(!e.isNavKeyPress()){
39444             return;
39445         }
39446         var g = this.grid, s = this.selection;
39447         if(!s){
39448             e.stopEvent();
39449             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39450             if(cell){
39451                 this.select(cell[0], cell[1]);
39452             }
39453             return;
39454         }
39455         var sm = this;
39456         var walk = function(row, col, step){
39457             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39458         };
39459         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39460         var newCell;
39461
39462       
39463
39464         switch(k){
39465             case e.TAB:
39466                 // handled by onEditorKey
39467                 if (g.isEditor && g.editing) {
39468                     return;
39469                 }
39470                 if(e.shiftKey) {
39471                     newCell = walk(r, c-1, -1);
39472                 } else {
39473                     newCell = walk(r, c+1, 1);
39474                 }
39475                 break;
39476             
39477             case e.DOWN:
39478                newCell = walk(r+1, c, 1);
39479                 break;
39480             
39481             case e.UP:
39482                 newCell = walk(r-1, c, -1);
39483                 break;
39484             
39485             case e.RIGHT:
39486                 newCell = walk(r, c+1, 1);
39487                 break;
39488             
39489             case e.LEFT:
39490                 newCell = walk(r, c-1, -1);
39491                 break;
39492             
39493             case e.ENTER:
39494                 
39495                 if(g.isEditor && !g.editing){
39496                    g.startEditing(r, c);
39497                    e.stopEvent();
39498                    return;
39499                 }
39500                 
39501                 
39502              break;
39503         };
39504         if(newCell){
39505             this.select(newCell[0], newCell[1]);
39506             e.stopEvent();
39507             
39508         }
39509     },
39510
39511     acceptsNav : function(row, col, cm){
39512         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39513     },
39514     /**
39515      * Selects a cell.
39516      * @param {Number} field (not used) - as it's normally used as a listener
39517      * @param {Number} e - event - fake it by using
39518      *
39519      * var e = Roo.EventObjectImpl.prototype;
39520      * e.keyCode = e.TAB
39521      *
39522      * 
39523      */
39524     onEditorKey : function(field, e){
39525         
39526         var k = e.getKey(),
39527             newCell,
39528             g = this.grid,
39529             ed = g.activeEditor,
39530             forward = false;
39531         ///Roo.log('onEditorKey' + k);
39532         
39533         
39534         if (this.enter_is_tab && k == e.ENTER) {
39535             k = e.TAB;
39536         }
39537         
39538         if(k == e.TAB){
39539             if(e.shiftKey){
39540                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39541             }else{
39542                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39543                 forward = true;
39544             }
39545             
39546             e.stopEvent();
39547             
39548         } else if(k == e.ENTER &&  !e.ctrlKey){
39549             ed.completeEdit();
39550             e.stopEvent();
39551             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39552         
39553                 } else if(k == e.ESC){
39554             ed.cancelEdit();
39555         }
39556                 
39557         if (newCell) {
39558             var ecall = { cell : newCell, forward : forward };
39559             this.fireEvent('beforeeditnext', ecall );
39560             newCell = ecall.cell;
39561                         forward = ecall.forward;
39562         }
39563                 
39564         if(newCell){
39565             //Roo.log('next cell after edit');
39566             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39567         } else if (forward) {
39568             // tabbed past last
39569             this.fireEvent.defer(100, this, ['tabend',this]);
39570         }
39571     }
39572 });/*
39573  * Based on:
39574  * Ext JS Library 1.1.1
39575  * Copyright(c) 2006-2007, Ext JS, LLC.
39576  *
39577  * Originally Released Under LGPL - original licence link has changed is not relivant.
39578  *
39579  * Fork - LGPL
39580  * <script type="text/javascript">
39581  */
39582  
39583 /**
39584  * @class Roo.grid.EditorGrid
39585  * @extends Roo.grid.Grid
39586  * Class for creating and editable grid.
39587  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39588  * The container MUST have some type of size defined for the grid to fill. The container will be 
39589  * automatically set to position relative if it isn't already.
39590  * @param {Object} dataSource The data model to bind to
39591  * @param {Object} colModel The column model with info about this grid's columns
39592  */
39593 Roo.grid.EditorGrid = function(container, config){
39594     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39595     this.getGridEl().addClass("xedit-grid");
39596
39597     if(!this.selModel){
39598         this.selModel = new Roo.grid.CellSelectionModel();
39599     }
39600
39601     this.activeEditor = null;
39602
39603         this.addEvents({
39604             /**
39605              * @event beforeedit
39606              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39607              * <ul style="padding:5px;padding-left:16px;">
39608              * <li>grid - This grid</li>
39609              * <li>record - The record being edited</li>
39610              * <li>field - The field name being edited</li>
39611              * <li>value - The value for the field being edited.</li>
39612              * <li>row - The grid row index</li>
39613              * <li>column - The grid column index</li>
39614              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39615              * </ul>
39616              * @param {Object} e An edit event (see above for description)
39617              */
39618             "beforeedit" : true,
39619             /**
39620              * @event afteredit
39621              * Fires after a cell is edited. <br />
39622              * <ul style="padding:5px;padding-left:16px;">
39623              * <li>grid - This grid</li>
39624              * <li>record - The record being edited</li>
39625              * <li>field - The field name being edited</li>
39626              * <li>value - The value being set</li>
39627              * <li>originalValue - The original value for the field, before the edit.</li>
39628              * <li>row - The grid row index</li>
39629              * <li>column - The grid column index</li>
39630              * </ul>
39631              * @param {Object} e An edit event (see above for description)
39632              */
39633             "afteredit" : true,
39634             /**
39635              * @event validateedit
39636              * Fires after a cell is edited, but before the value is set in the record. 
39637          * You can use this to modify the value being set in the field, Return false
39638              * to cancel the change. The edit event object has the following properties <br />
39639              * <ul style="padding:5px;padding-left:16px;">
39640          * <li>editor - This editor</li>
39641              * <li>grid - This grid</li>
39642              * <li>record - The record being edited</li>
39643              * <li>field - The field name being edited</li>
39644              * <li>value - The value being set</li>
39645              * <li>originalValue - The original value for the field, before the edit.</li>
39646              * <li>row - The grid row index</li>
39647              * <li>column - The grid column index</li>
39648              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39649              * </ul>
39650              * @param {Object} e An edit event (see above for description)
39651              */
39652             "validateedit" : true
39653         });
39654     this.on("bodyscroll", this.stopEditing,  this);
39655     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39656 };
39657
39658 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39659     /**
39660      * @cfg {Number} clicksToEdit
39661      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39662      */
39663     clicksToEdit: 2,
39664
39665     // private
39666     isEditor : true,
39667     // private
39668     trackMouseOver: false, // causes very odd FF errors
39669
39670     onCellDblClick : function(g, row, col){
39671         this.startEditing(row, col);
39672     },
39673
39674     onEditComplete : function(ed, value, startValue){
39675         this.editing = false;
39676         this.activeEditor = null;
39677         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39678         var r = ed.record;
39679         var field = this.colModel.getDataIndex(ed.col);
39680         var e = {
39681             grid: this,
39682             record: r,
39683             field: field,
39684             originalValue: startValue,
39685             value: value,
39686             row: ed.row,
39687             column: ed.col,
39688             cancel:false,
39689             editor: ed
39690         };
39691         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39692         cell.show();
39693           
39694         if(String(value) !== String(startValue)){
39695             
39696             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39697                 r.set(field, e.value);
39698                 // if we are dealing with a combo box..
39699                 // then we also set the 'name' colum to be the displayField
39700                 if (ed.field.displayField && ed.field.name) {
39701                     r.set(ed.field.name, ed.field.el.dom.value);
39702                 }
39703                 
39704                 delete e.cancel; //?? why!!!
39705                 this.fireEvent("afteredit", e);
39706             }
39707         } else {
39708             this.fireEvent("afteredit", e); // always fire it!
39709         }
39710         this.view.focusCell(ed.row, ed.col);
39711     },
39712
39713     /**
39714      * Starts editing the specified for the specified row/column
39715      * @param {Number} rowIndex
39716      * @param {Number} colIndex
39717      */
39718     startEditing : function(row, col){
39719         this.stopEditing();
39720         if(this.colModel.isCellEditable(col, row)){
39721             this.view.ensureVisible(row, col, true);
39722           
39723             var r = this.dataSource.getAt(row);
39724             var field = this.colModel.getDataIndex(col);
39725             var cell = Roo.get(this.view.getCell(row,col));
39726             var e = {
39727                 grid: this,
39728                 record: r,
39729                 field: field,
39730                 value: r.data[field],
39731                 row: row,
39732                 column: col,
39733                 cancel:false 
39734             };
39735             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39736                 this.editing = true;
39737                 var ed = this.colModel.getCellEditor(col, row);
39738                 
39739                 if (!ed) {
39740                     return;
39741                 }
39742                 if(!ed.rendered){
39743                     ed.render(ed.parentEl || document.body);
39744                 }
39745                 ed.field.reset();
39746                
39747                 cell.hide();
39748                 
39749                 (function(){ // complex but required for focus issues in safari, ie and opera
39750                     ed.row = row;
39751                     ed.col = col;
39752                     ed.record = r;
39753                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39754                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39755                     this.activeEditor = ed;
39756                     var v = r.data[field];
39757                     ed.startEdit(this.view.getCell(row, col), v);
39758                     // combo's with 'displayField and name set
39759                     if (ed.field.displayField && ed.field.name) {
39760                         ed.field.el.dom.value = r.data[ed.field.name];
39761                     }
39762                     
39763                     
39764                 }).defer(50, this);
39765             }
39766         }
39767     },
39768         
39769     /**
39770      * Stops any active editing
39771      */
39772     stopEditing : function(){
39773         if(this.activeEditor){
39774             this.activeEditor.completeEdit();
39775         }
39776         this.activeEditor = null;
39777     },
39778         
39779          /**
39780      * Called to get grid's drag proxy text, by default returns this.ddText.
39781      * @return {String}
39782      */
39783     getDragDropText : function(){
39784         var count = this.selModel.getSelectedCell() ? 1 : 0;
39785         return String.format(this.ddText, count, count == 1 ? '' : 's');
39786     }
39787         
39788 });/*
39789  * Based on:
39790  * Ext JS Library 1.1.1
39791  * Copyright(c) 2006-2007, Ext JS, LLC.
39792  *
39793  * Originally Released Under LGPL - original licence link has changed is not relivant.
39794  *
39795  * Fork - LGPL
39796  * <script type="text/javascript">
39797  */
39798
39799 // private - not really -- you end up using it !
39800 // This is a support class used internally by the Grid components
39801
39802 /**
39803  * @class Roo.grid.GridEditor
39804  * @extends Roo.Editor
39805  * Class for creating and editable grid elements.
39806  * @param {Object} config any settings (must include field)
39807  */
39808 Roo.grid.GridEditor = function(field, config){
39809     if (!config && field.field) {
39810         config = field;
39811         field = Roo.factory(config.field, Roo.form);
39812     }
39813     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39814     field.monitorTab = false;
39815 };
39816
39817 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39818     
39819     /**
39820      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39821      */
39822     
39823     alignment: "tl-tl",
39824     autoSize: "width",
39825     hideEl : false,
39826     cls: "x-small-editor x-grid-editor",
39827     shim:false,
39828     shadow:"frame"
39829 });/*
39830  * Based on:
39831  * Ext JS Library 1.1.1
39832  * Copyright(c) 2006-2007, Ext JS, LLC.
39833  *
39834  * Originally Released Under LGPL - original licence link has changed is not relivant.
39835  *
39836  * Fork - LGPL
39837  * <script type="text/javascript">
39838  */
39839   
39840
39841   
39842 Roo.grid.PropertyRecord = Roo.data.Record.create([
39843     {name:'name',type:'string'},  'value'
39844 ]);
39845
39846
39847 Roo.grid.PropertyStore = function(grid, source){
39848     this.grid = grid;
39849     this.store = new Roo.data.Store({
39850         recordType : Roo.grid.PropertyRecord
39851     });
39852     this.store.on('update', this.onUpdate,  this);
39853     if(source){
39854         this.setSource(source);
39855     }
39856     Roo.grid.PropertyStore.superclass.constructor.call(this);
39857 };
39858
39859
39860
39861 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39862     setSource : function(o){
39863         this.source = o;
39864         this.store.removeAll();
39865         var data = [];
39866         for(var k in o){
39867             if(this.isEditableValue(o[k])){
39868                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39869             }
39870         }
39871         this.store.loadRecords({records: data}, {}, true);
39872     },
39873
39874     onUpdate : function(ds, record, type){
39875         if(type == Roo.data.Record.EDIT){
39876             var v = record.data['value'];
39877             var oldValue = record.modified['value'];
39878             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39879                 this.source[record.id] = v;
39880                 record.commit();
39881                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39882             }else{
39883                 record.reject();
39884             }
39885         }
39886     },
39887
39888     getProperty : function(row){
39889        return this.store.getAt(row);
39890     },
39891
39892     isEditableValue: function(val){
39893         if(val && val instanceof Date){
39894             return true;
39895         }else if(typeof val == 'object' || typeof val == 'function'){
39896             return false;
39897         }
39898         return true;
39899     },
39900
39901     setValue : function(prop, value){
39902         this.source[prop] = value;
39903         this.store.getById(prop).set('value', value);
39904     },
39905
39906     getSource : function(){
39907         return this.source;
39908     }
39909 });
39910
39911 Roo.grid.PropertyColumnModel = function(grid, store){
39912     this.grid = grid;
39913     var g = Roo.grid;
39914     g.PropertyColumnModel.superclass.constructor.call(this, [
39915         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39916         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39917     ]);
39918     this.store = store;
39919     this.bselect = Roo.DomHelper.append(document.body, {
39920         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39921             {tag: 'option', value: 'true', html: 'true'},
39922             {tag: 'option', value: 'false', html: 'false'}
39923         ]
39924     });
39925     Roo.id(this.bselect);
39926     var f = Roo.form;
39927     this.editors = {
39928         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39929         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39930         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39931         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39932         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39933     };
39934     this.renderCellDelegate = this.renderCell.createDelegate(this);
39935     this.renderPropDelegate = this.renderProp.createDelegate(this);
39936 };
39937
39938 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39939     
39940     
39941     nameText : 'Name',
39942     valueText : 'Value',
39943     
39944     dateFormat : 'm/j/Y',
39945     
39946     
39947     renderDate : function(dateVal){
39948         return dateVal.dateFormat(this.dateFormat);
39949     },
39950
39951     renderBool : function(bVal){
39952         return bVal ? 'true' : 'false';
39953     },
39954
39955     isCellEditable : function(colIndex, rowIndex){
39956         return colIndex == 1;
39957     },
39958
39959     getRenderer : function(col){
39960         return col == 1 ?
39961             this.renderCellDelegate : this.renderPropDelegate;
39962     },
39963
39964     renderProp : function(v){
39965         return this.getPropertyName(v);
39966     },
39967
39968     renderCell : function(val){
39969         var rv = val;
39970         if(val instanceof Date){
39971             rv = this.renderDate(val);
39972         }else if(typeof val == 'boolean'){
39973             rv = this.renderBool(val);
39974         }
39975         return Roo.util.Format.htmlEncode(rv);
39976     },
39977
39978     getPropertyName : function(name){
39979         var pn = this.grid.propertyNames;
39980         return pn && pn[name] ? pn[name] : name;
39981     },
39982
39983     getCellEditor : function(colIndex, rowIndex){
39984         var p = this.store.getProperty(rowIndex);
39985         var n = p.data['name'], val = p.data['value'];
39986         
39987         if(typeof(this.grid.customEditors[n]) == 'string'){
39988             return this.editors[this.grid.customEditors[n]];
39989         }
39990         if(typeof(this.grid.customEditors[n]) != 'undefined'){
39991             return this.grid.customEditors[n];
39992         }
39993         if(val instanceof Date){
39994             return this.editors['date'];
39995         }else if(typeof val == 'number'){
39996             return this.editors['number'];
39997         }else if(typeof val == 'boolean'){
39998             return this.editors['boolean'];
39999         }else{
40000             return this.editors['string'];
40001         }
40002     }
40003 });
40004
40005 /**
40006  * @class Roo.grid.PropertyGrid
40007  * @extends Roo.grid.EditorGrid
40008  * This class represents the  interface of a component based property grid control.
40009  * <br><br>Usage:<pre><code>
40010  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40011       
40012  });
40013  // set any options
40014  grid.render();
40015  * </code></pre>
40016   
40017  * @constructor
40018  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40019  * The container MUST have some type of size defined for the grid to fill. The container will be
40020  * automatically set to position relative if it isn't already.
40021  * @param {Object} config A config object that sets properties on this grid.
40022  */
40023 Roo.grid.PropertyGrid = function(container, config){
40024     config = config || {};
40025     var store = new Roo.grid.PropertyStore(this);
40026     this.store = store;
40027     var cm = new Roo.grid.PropertyColumnModel(this, store);
40028     store.store.sort('name', 'ASC');
40029     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40030         ds: store.store,
40031         cm: cm,
40032         enableColLock:false,
40033         enableColumnMove:false,
40034         stripeRows:false,
40035         trackMouseOver: false,
40036         clicksToEdit:1
40037     }, config));
40038     this.getGridEl().addClass('x-props-grid');
40039     this.lastEditRow = null;
40040     this.on('columnresize', this.onColumnResize, this);
40041     this.addEvents({
40042          /**
40043              * @event beforepropertychange
40044              * Fires before a property changes (return false to stop?)
40045              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40046              * @param {String} id Record Id
40047              * @param {String} newval New Value
40048          * @param {String} oldval Old Value
40049              */
40050         "beforepropertychange": true,
40051         /**
40052              * @event propertychange
40053              * Fires after a property changes
40054              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40055              * @param {String} id Record Id
40056              * @param {String} newval New Value
40057          * @param {String} oldval Old Value
40058              */
40059         "propertychange": true
40060     });
40061     this.customEditors = this.customEditors || {};
40062 };
40063 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40064     
40065      /**
40066      * @cfg {Object} customEditors map of colnames=> custom editors.
40067      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40068      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40069      * false disables editing of the field.
40070          */
40071     
40072       /**
40073      * @cfg {Object} propertyNames map of property Names to their displayed value
40074          */
40075     
40076     render : function(){
40077         Roo.grid.PropertyGrid.superclass.render.call(this);
40078         this.autoSize.defer(100, this);
40079     },
40080
40081     autoSize : function(){
40082         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40083         if(this.view){
40084             this.view.fitColumns();
40085         }
40086     },
40087
40088     onColumnResize : function(){
40089         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40090         this.autoSize();
40091     },
40092     /**
40093      * Sets the data for the Grid
40094      * accepts a Key => Value object of all the elements avaiable.
40095      * @param {Object} data  to appear in grid.
40096      */
40097     setSource : function(source){
40098         this.store.setSource(source);
40099         //this.autoSize();
40100     },
40101     /**
40102      * Gets all the data from the grid.
40103      * @return {Object} data  data stored in grid
40104      */
40105     getSource : function(){
40106         return this.store.getSource();
40107     }
40108 });/*
40109   
40110  * Licence LGPL
40111  
40112  */
40113  
40114 /**
40115  * @class Roo.grid.Calendar
40116  * @extends Roo.util.Grid
40117  * This class extends the Grid to provide a calendar widget
40118  * <br><br>Usage:<pre><code>
40119  var grid = new Roo.grid.Calendar("my-container-id", {
40120      ds: myDataStore,
40121      cm: myColModel,
40122      selModel: mySelectionModel,
40123      autoSizeColumns: true,
40124      monitorWindowResize: false,
40125      trackMouseOver: true
40126      eventstore : real data store..
40127  });
40128  // set any options
40129  grid.render();
40130   
40131   * @constructor
40132  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40133  * The container MUST have some type of size defined for the grid to fill. The container will be
40134  * automatically set to position relative if it isn't already.
40135  * @param {Object} config A config object that sets properties on this grid.
40136  */
40137 Roo.grid.Calendar = function(container, config){
40138         // initialize the container
40139         this.container = Roo.get(container);
40140         this.container.update("");
40141         this.container.setStyle("overflow", "hidden");
40142     this.container.addClass('x-grid-container');
40143
40144     this.id = this.container.id;
40145
40146     Roo.apply(this, config);
40147     // check and correct shorthanded configs
40148     
40149     var rows = [];
40150     var d =1;
40151     for (var r = 0;r < 6;r++) {
40152         
40153         rows[r]=[];
40154         for (var c =0;c < 7;c++) {
40155             rows[r][c]= '';
40156         }
40157     }
40158     if (this.eventStore) {
40159         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40160         this.eventStore.on('load',this.onLoad, this);
40161         this.eventStore.on('beforeload',this.clearEvents, this);
40162          
40163     }
40164     
40165     this.dataSource = new Roo.data.Store({
40166             proxy: new Roo.data.MemoryProxy(rows),
40167             reader: new Roo.data.ArrayReader({}, [
40168                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40169     });
40170
40171     this.dataSource.load();
40172     this.ds = this.dataSource;
40173     this.ds.xmodule = this.xmodule || false;
40174     
40175     
40176     var cellRender = function(v,x,r)
40177     {
40178         return String.format(
40179             '<div class="fc-day  fc-widget-content"><div>' +
40180                 '<div class="fc-event-container"></div>' +
40181                 '<div class="fc-day-number">{0}</div>'+
40182                 
40183                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40184             '</div></div>', v);
40185     
40186     }
40187     
40188     
40189     this.colModel = new Roo.grid.ColumnModel( [
40190         {
40191             xtype: 'ColumnModel',
40192             xns: Roo.grid,
40193             dataIndex : 'weekday0',
40194             header : 'Sunday',
40195             renderer : cellRender
40196         },
40197         {
40198             xtype: 'ColumnModel',
40199             xns: Roo.grid,
40200             dataIndex : 'weekday1',
40201             header : 'Monday',
40202             renderer : cellRender
40203         },
40204         {
40205             xtype: 'ColumnModel',
40206             xns: Roo.grid,
40207             dataIndex : 'weekday2',
40208             header : 'Tuesday',
40209             renderer : cellRender
40210         },
40211         {
40212             xtype: 'ColumnModel',
40213             xns: Roo.grid,
40214             dataIndex : 'weekday3',
40215             header : 'Wednesday',
40216             renderer : cellRender
40217         },
40218         {
40219             xtype: 'ColumnModel',
40220             xns: Roo.grid,
40221             dataIndex : 'weekday4',
40222             header : 'Thursday',
40223             renderer : cellRender
40224         },
40225         {
40226             xtype: 'ColumnModel',
40227             xns: Roo.grid,
40228             dataIndex : 'weekday5',
40229             header : 'Friday',
40230             renderer : cellRender
40231         },
40232         {
40233             xtype: 'ColumnModel',
40234             xns: Roo.grid,
40235             dataIndex : 'weekday6',
40236             header : 'Saturday',
40237             renderer : cellRender
40238         }
40239     ]);
40240     this.cm = this.colModel;
40241     this.cm.xmodule = this.xmodule || false;
40242  
40243         
40244           
40245     //this.selModel = new Roo.grid.CellSelectionModel();
40246     //this.sm = this.selModel;
40247     //this.selModel.init(this);
40248     
40249     
40250     if(this.width){
40251         this.container.setWidth(this.width);
40252     }
40253
40254     if(this.height){
40255         this.container.setHeight(this.height);
40256     }
40257     /** @private */
40258         this.addEvents({
40259         // raw events
40260         /**
40261          * @event click
40262          * The raw click event for the entire grid.
40263          * @param {Roo.EventObject} e
40264          */
40265         "click" : true,
40266         /**
40267          * @event dblclick
40268          * The raw dblclick event for the entire grid.
40269          * @param {Roo.EventObject} e
40270          */
40271         "dblclick" : true,
40272         /**
40273          * @event contextmenu
40274          * The raw contextmenu event for the entire grid.
40275          * @param {Roo.EventObject} e
40276          */
40277         "contextmenu" : true,
40278         /**
40279          * @event mousedown
40280          * The raw mousedown event for the entire grid.
40281          * @param {Roo.EventObject} e
40282          */
40283         "mousedown" : true,
40284         /**
40285          * @event mouseup
40286          * The raw mouseup event for the entire grid.
40287          * @param {Roo.EventObject} e
40288          */
40289         "mouseup" : true,
40290         /**
40291          * @event mouseover
40292          * The raw mouseover event for the entire grid.
40293          * @param {Roo.EventObject} e
40294          */
40295         "mouseover" : true,
40296         /**
40297          * @event mouseout
40298          * The raw mouseout event for the entire grid.
40299          * @param {Roo.EventObject} e
40300          */
40301         "mouseout" : true,
40302         /**
40303          * @event keypress
40304          * The raw keypress event for the entire grid.
40305          * @param {Roo.EventObject} e
40306          */
40307         "keypress" : true,
40308         /**
40309          * @event keydown
40310          * The raw keydown event for the entire grid.
40311          * @param {Roo.EventObject} e
40312          */
40313         "keydown" : true,
40314
40315         // custom events
40316
40317         /**
40318          * @event cellclick
40319          * Fires when a cell is clicked
40320          * @param {Grid} this
40321          * @param {Number} rowIndex
40322          * @param {Number} columnIndex
40323          * @param {Roo.EventObject} e
40324          */
40325         "cellclick" : true,
40326         /**
40327          * @event celldblclick
40328          * Fires when a cell is double clicked
40329          * @param {Grid} this
40330          * @param {Number} rowIndex
40331          * @param {Number} columnIndex
40332          * @param {Roo.EventObject} e
40333          */
40334         "celldblclick" : true,
40335         /**
40336          * @event rowclick
40337          * Fires when a row is clicked
40338          * @param {Grid} this
40339          * @param {Number} rowIndex
40340          * @param {Roo.EventObject} e
40341          */
40342         "rowclick" : true,
40343         /**
40344          * @event rowdblclick
40345          * Fires when a row is double clicked
40346          * @param {Grid} this
40347          * @param {Number} rowIndex
40348          * @param {Roo.EventObject} e
40349          */
40350         "rowdblclick" : true,
40351         /**
40352          * @event headerclick
40353          * Fires when a header is clicked
40354          * @param {Grid} this
40355          * @param {Number} columnIndex
40356          * @param {Roo.EventObject} e
40357          */
40358         "headerclick" : true,
40359         /**
40360          * @event headerdblclick
40361          * Fires when a header cell is double clicked
40362          * @param {Grid} this
40363          * @param {Number} columnIndex
40364          * @param {Roo.EventObject} e
40365          */
40366         "headerdblclick" : true,
40367         /**
40368          * @event rowcontextmenu
40369          * Fires when a row is right clicked
40370          * @param {Grid} this
40371          * @param {Number} rowIndex
40372          * @param {Roo.EventObject} e
40373          */
40374         "rowcontextmenu" : true,
40375         /**
40376          * @event cellcontextmenu
40377          * Fires when a cell is right clicked
40378          * @param {Grid} this
40379          * @param {Number} rowIndex
40380          * @param {Number} cellIndex
40381          * @param {Roo.EventObject} e
40382          */
40383          "cellcontextmenu" : true,
40384         /**
40385          * @event headercontextmenu
40386          * Fires when a header is right clicked
40387          * @param {Grid} this
40388          * @param {Number} columnIndex
40389          * @param {Roo.EventObject} e
40390          */
40391         "headercontextmenu" : true,
40392         /**
40393          * @event bodyscroll
40394          * Fires when the body element is scrolled
40395          * @param {Number} scrollLeft
40396          * @param {Number} scrollTop
40397          */
40398         "bodyscroll" : true,
40399         /**
40400          * @event columnresize
40401          * Fires when the user resizes a column
40402          * @param {Number} columnIndex
40403          * @param {Number} newSize
40404          */
40405         "columnresize" : true,
40406         /**
40407          * @event columnmove
40408          * Fires when the user moves a column
40409          * @param {Number} oldIndex
40410          * @param {Number} newIndex
40411          */
40412         "columnmove" : true,
40413         /**
40414          * @event startdrag
40415          * Fires when row(s) start being dragged
40416          * @param {Grid} this
40417          * @param {Roo.GridDD} dd The drag drop object
40418          * @param {event} e The raw browser event
40419          */
40420         "startdrag" : true,
40421         /**
40422          * @event enddrag
40423          * Fires when a drag operation is complete
40424          * @param {Grid} this
40425          * @param {Roo.GridDD} dd The drag drop object
40426          * @param {event} e The raw browser event
40427          */
40428         "enddrag" : true,
40429         /**
40430          * @event dragdrop
40431          * Fires when dragged row(s) are dropped on a valid DD target
40432          * @param {Grid} this
40433          * @param {Roo.GridDD} dd The drag drop object
40434          * @param {String} targetId The target drag drop object
40435          * @param {event} e The raw browser event
40436          */
40437         "dragdrop" : true,
40438         /**
40439          * @event dragover
40440          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40441          * @param {Grid} this
40442          * @param {Roo.GridDD} dd The drag drop object
40443          * @param {String} targetId The target drag drop object
40444          * @param {event} e The raw browser event
40445          */
40446         "dragover" : true,
40447         /**
40448          * @event dragenter
40449          *  Fires when the dragged row(s) first cross another DD target while being dragged
40450          * @param {Grid} this
40451          * @param {Roo.GridDD} dd The drag drop object
40452          * @param {String} targetId The target drag drop object
40453          * @param {event} e The raw browser event
40454          */
40455         "dragenter" : true,
40456         /**
40457          * @event dragout
40458          * Fires when the dragged row(s) leave another DD target while being dragged
40459          * @param {Grid} this
40460          * @param {Roo.GridDD} dd The drag drop object
40461          * @param {String} targetId The target drag drop object
40462          * @param {event} e The raw browser event
40463          */
40464         "dragout" : true,
40465         /**
40466          * @event rowclass
40467          * Fires when a row is rendered, so you can change add a style to it.
40468          * @param {GridView} gridview   The grid view
40469          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
40470          */
40471         'rowclass' : true,
40472
40473         /**
40474          * @event render
40475          * Fires when the grid is rendered
40476          * @param {Grid} grid
40477          */
40478         'render' : true,
40479             /**
40480              * @event select
40481              * Fires when a date is selected
40482              * @param {DatePicker} this
40483              * @param {Date} date The selected date
40484              */
40485         'select': true,
40486         /**
40487              * @event monthchange
40488              * Fires when the displayed month changes 
40489              * @param {DatePicker} this
40490              * @param {Date} date The selected month
40491              */
40492         'monthchange': true,
40493         /**
40494              * @event evententer
40495              * Fires when mouse over an event
40496              * @param {Calendar} this
40497              * @param {event} Event
40498              */
40499         'evententer': true,
40500         /**
40501              * @event eventleave
40502              * Fires when the mouse leaves an
40503              * @param {Calendar} this
40504              * @param {event}
40505              */
40506         'eventleave': true,
40507         /**
40508              * @event eventclick
40509              * Fires when the mouse click an
40510              * @param {Calendar} this
40511              * @param {event}
40512              */
40513         'eventclick': true,
40514         /**
40515              * @event eventrender
40516              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
40517              * @param {Calendar} this
40518              * @param {data} data to be modified
40519              */
40520         'eventrender': true
40521         
40522     });
40523
40524     Roo.grid.Grid.superclass.constructor.call(this);
40525     this.on('render', function() {
40526         this.view.el.addClass('x-grid-cal'); 
40527         
40528         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
40529
40530     },this);
40531     
40532     if (!Roo.grid.Calendar.style) {
40533         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
40534             
40535             
40536             '.x-grid-cal .x-grid-col' :  {
40537                 height: 'auto !important',
40538                 'vertical-align': 'top'
40539             },
40540             '.x-grid-cal  .fc-event-hori' : {
40541                 height: '14px'
40542             }
40543              
40544             
40545         }, Roo.id());
40546     }
40547
40548     
40549     
40550 };
40551 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
40552     /**
40553      * @cfg {Store} eventStore The store that loads events.
40554      */
40555     eventStore : 25,
40556
40557      
40558     activeDate : false,
40559     startDay : 0,
40560     autoWidth : true,
40561     monitorWindowResize : false,
40562
40563     
40564     resizeColumns : function() {
40565         var col = (this.view.el.getWidth() / 7) - 3;
40566         // loop through cols, and setWidth
40567         for(var i =0 ; i < 7 ; i++){
40568             this.cm.setColumnWidth(i, col);
40569         }
40570     },
40571      setDate :function(date) {
40572         
40573         Roo.log('setDate?');
40574         
40575         this.resizeColumns();
40576         var vd = this.activeDate;
40577         this.activeDate = date;
40578 //        if(vd && this.el){
40579 //            var t = date.getTime();
40580 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
40581 //                Roo.log('using add remove');
40582 //                
40583 //                this.fireEvent('monthchange', this, date);
40584 //                
40585 //                this.cells.removeClass("fc-state-highlight");
40586 //                this.cells.each(function(c){
40587 //                   if(c.dateValue == t){
40588 //                       c.addClass("fc-state-highlight");
40589 //                       setTimeout(function(){
40590 //                            try{c.dom.firstChild.focus();}catch(e){}
40591 //                       }, 50);
40592 //                       return false;
40593 //                   }
40594 //                   return true;
40595 //                });
40596 //                return;
40597 //            }
40598 //        }
40599         
40600         var days = date.getDaysInMonth();
40601         
40602         var firstOfMonth = date.getFirstDateOfMonth();
40603         var startingPos = firstOfMonth.getDay()-this.startDay;
40604         
40605         if(startingPos < this.startDay){
40606             startingPos += 7;
40607         }
40608         
40609         var pm = date.add(Date.MONTH, -1);
40610         var prevStart = pm.getDaysInMonth()-startingPos;
40611 //        
40612         
40613         
40614         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40615         
40616         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
40617         //this.cells.addClassOnOver('fc-state-hover');
40618         
40619         var cells = this.cells.elements;
40620         var textEls = this.textNodes;
40621         
40622         //Roo.each(cells, function(cell){
40623         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
40624         //});
40625         
40626         days += startingPos;
40627
40628         // convert everything to numbers so it's fast
40629         var day = 86400000;
40630         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
40631         //Roo.log(d);
40632         //Roo.log(pm);
40633         //Roo.log(prevStart);
40634         
40635         var today = new Date().clearTime().getTime();
40636         var sel = date.clearTime().getTime();
40637         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
40638         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
40639         var ddMatch = this.disabledDatesRE;
40640         var ddText = this.disabledDatesText;
40641         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
40642         var ddaysText = this.disabledDaysText;
40643         var format = this.format;
40644         
40645         var setCellClass = function(cal, cell){
40646             
40647             //Roo.log('set Cell Class');
40648             cell.title = "";
40649             var t = d.getTime();
40650             
40651             //Roo.log(d);
40652             
40653             
40654             cell.dateValue = t;
40655             if(t == today){
40656                 cell.className += " fc-today";
40657                 cell.className += " fc-state-highlight";
40658                 cell.title = cal.todayText;
40659             }
40660             if(t == sel){
40661                 // disable highlight in other month..
40662                 cell.className += " fc-state-highlight";
40663                 
40664             }
40665             // disabling
40666             if(t < min) {
40667                 //cell.className = " fc-state-disabled";
40668                 cell.title = cal.minText;
40669                 return;
40670             }
40671             if(t > max) {
40672                 //cell.className = " fc-state-disabled";
40673                 cell.title = cal.maxText;
40674                 return;
40675             }
40676             if(ddays){
40677                 if(ddays.indexOf(d.getDay()) != -1){
40678                     // cell.title = ddaysText;
40679                    // cell.className = " fc-state-disabled";
40680                 }
40681             }
40682             if(ddMatch && format){
40683                 var fvalue = d.dateFormat(format);
40684                 if(ddMatch.test(fvalue)){
40685                     cell.title = ddText.replace("%0", fvalue);
40686                    cell.className = " fc-state-disabled";
40687                 }
40688             }
40689             
40690             if (!cell.initialClassName) {
40691                 cell.initialClassName = cell.dom.className;
40692             }
40693             
40694             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
40695         };
40696
40697         var i = 0;
40698         
40699         for(; i < startingPos; i++) {
40700             cells[i].dayName =  (++prevStart);
40701             Roo.log(textEls[i]);
40702             d.setDate(d.getDate()+1);
40703             
40704             //cells[i].className = "fc-past fc-other-month";
40705             setCellClass(this, cells[i]);
40706         }
40707         
40708         var intDay = 0;
40709         
40710         for(; i < days; i++){
40711             intDay = i - startingPos + 1;
40712             cells[i].dayName =  (intDay);
40713             d.setDate(d.getDate()+1);
40714             
40715             cells[i].className = ''; // "x-date-active";
40716             setCellClass(this, cells[i]);
40717         }
40718         var extraDays = 0;
40719         
40720         for(; i < 42; i++) {
40721             //textEls[i].innerHTML = (++extraDays);
40722             
40723             d.setDate(d.getDate()+1);
40724             cells[i].dayName = (++extraDays);
40725             cells[i].className = "fc-future fc-other-month";
40726             setCellClass(this, cells[i]);
40727         }
40728         
40729         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
40730         
40731         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
40732         
40733         // this will cause all the cells to mis
40734         var rows= [];
40735         var i =0;
40736         for (var r = 0;r < 6;r++) {
40737             for (var c =0;c < 7;c++) {
40738                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
40739             }    
40740         }
40741         
40742         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40743         for(i=0;i<cells.length;i++) {
40744             
40745             this.cells.elements[i].dayName = cells[i].dayName ;
40746             this.cells.elements[i].className = cells[i].className;
40747             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
40748             this.cells.elements[i].title = cells[i].title ;
40749             this.cells.elements[i].dateValue = cells[i].dateValue ;
40750         }
40751         
40752         
40753         
40754         
40755         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
40756         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
40757         
40758         ////if(totalRows != 6){
40759             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
40760            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
40761        // }
40762         
40763         this.fireEvent('monthchange', this, date);
40764         
40765         
40766     },
40767  /**
40768      * Returns the grid's SelectionModel.
40769      * @return {SelectionModel}
40770      */
40771     getSelectionModel : function(){
40772         if(!this.selModel){
40773             this.selModel = new Roo.grid.CellSelectionModel();
40774         }
40775         return this.selModel;
40776     },
40777
40778     load: function() {
40779         this.eventStore.load()
40780         
40781         
40782         
40783     },
40784     
40785     findCell : function(dt) {
40786         dt = dt.clearTime().getTime();
40787         var ret = false;
40788         this.cells.each(function(c){
40789             //Roo.log("check " +c.dateValue + '?=' + dt);
40790             if(c.dateValue == dt){
40791                 ret = c;
40792                 return false;
40793             }
40794             return true;
40795         });
40796         
40797         return ret;
40798     },
40799     
40800     findCells : function(rec) {
40801         var s = rec.data.start_dt.clone().clearTime().getTime();
40802        // Roo.log(s);
40803         var e= rec.data.end_dt.clone().clearTime().getTime();
40804        // Roo.log(e);
40805         var ret = [];
40806         this.cells.each(function(c){
40807              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
40808             
40809             if(c.dateValue > e){
40810                 return ;
40811             }
40812             if(c.dateValue < s){
40813                 return ;
40814             }
40815             ret.push(c);
40816         });
40817         
40818         return ret;    
40819     },
40820     
40821     findBestRow: function(cells)
40822     {
40823         var ret = 0;
40824         
40825         for (var i =0 ; i < cells.length;i++) {
40826             ret  = Math.max(cells[i].rows || 0,ret);
40827         }
40828         return ret;
40829         
40830     },
40831     
40832     
40833     addItem : function(rec)
40834     {
40835         // look for vertical location slot in
40836         var cells = this.findCells(rec);
40837         
40838         rec.row = this.findBestRow(cells);
40839         
40840         // work out the location.
40841         
40842         var crow = false;
40843         var rows = [];
40844         for(var i =0; i < cells.length; i++) {
40845             if (!crow) {
40846                 crow = {
40847                     start : cells[i],
40848                     end :  cells[i]
40849                 };
40850                 continue;
40851             }
40852             if (crow.start.getY() == cells[i].getY()) {
40853                 // on same row.
40854                 crow.end = cells[i];
40855                 continue;
40856             }
40857             // different row.
40858             rows.push(crow);
40859             crow = {
40860                 start: cells[i],
40861                 end : cells[i]
40862             };
40863             
40864         }
40865         
40866         rows.push(crow);
40867         rec.els = [];
40868         rec.rows = rows;
40869         rec.cells = cells;
40870         for (var i = 0; i < cells.length;i++) {
40871             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
40872             
40873         }
40874         
40875         
40876     },
40877     
40878     clearEvents: function() {
40879         
40880         if (!this.eventStore.getCount()) {
40881             return;
40882         }
40883         // reset number of rows in cells.
40884         Roo.each(this.cells.elements, function(c){
40885             c.rows = 0;
40886         });
40887         
40888         this.eventStore.each(function(e) {
40889             this.clearEvent(e);
40890         },this);
40891         
40892     },
40893     
40894     clearEvent : function(ev)
40895     {
40896         if (ev.els) {
40897             Roo.each(ev.els, function(el) {
40898                 el.un('mouseenter' ,this.onEventEnter, this);
40899                 el.un('mouseleave' ,this.onEventLeave, this);
40900                 el.remove();
40901             },this);
40902             ev.els = [];
40903         }
40904     },
40905     
40906     
40907     renderEvent : function(ev,ctr) {
40908         if (!ctr) {
40909              ctr = this.view.el.select('.fc-event-container',true).first();
40910         }
40911         
40912          
40913         this.clearEvent(ev);
40914             //code
40915        
40916         
40917         
40918         ev.els = [];
40919         var cells = ev.cells;
40920         var rows = ev.rows;
40921         this.fireEvent('eventrender', this, ev);
40922         
40923         for(var i =0; i < rows.length; i++) {
40924             
40925             cls = '';
40926             if (i == 0) {
40927                 cls += ' fc-event-start';
40928             }
40929             if ((i+1) == rows.length) {
40930                 cls += ' fc-event-end';
40931             }
40932             
40933             //Roo.log(ev.data);
40934             // how many rows should it span..
40935             var cg = this.eventTmpl.append(ctr,Roo.apply({
40936                 fccls : cls
40937                 
40938             }, ev.data) , true);
40939             
40940             
40941             cg.on('mouseenter' ,this.onEventEnter, this, ev);
40942             cg.on('mouseleave' ,this.onEventLeave, this, ev);
40943             cg.on('click', this.onEventClick, this, ev);
40944             
40945             ev.els.push(cg);
40946             
40947             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
40948             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
40949             //Roo.log(cg);
40950              
40951             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
40952             cg.setWidth(ebox.right - sbox.x -2);
40953         }
40954     },
40955     
40956     renderEvents: function()
40957     {   
40958         // first make sure there is enough space..
40959         
40960         if (!this.eventTmpl) {
40961             this.eventTmpl = new Roo.Template(
40962                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
40963                     '<div class="fc-event-inner">' +
40964                         '<span class="fc-event-time">{time}</span>' +
40965                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
40966                     '</div>' +
40967                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
40968                 '</div>'
40969             );
40970                 
40971         }
40972                
40973         
40974         
40975         this.cells.each(function(c) {
40976             //Roo.log(c.select('.fc-day-content div',true).first());
40977             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
40978         });
40979         
40980         var ctr = this.view.el.select('.fc-event-container',true).first();
40981         
40982         var cls;
40983         this.eventStore.each(function(ev){
40984             
40985             this.renderEvent(ev);
40986              
40987              
40988         }, this);
40989         this.view.layout();
40990         
40991     },
40992     
40993     onEventEnter: function (e, el,event,d) {
40994         this.fireEvent('evententer', this, el, event);
40995     },
40996     
40997     onEventLeave: function (e, el,event,d) {
40998         this.fireEvent('eventleave', this, el, event);
40999     },
41000     
41001     onEventClick: function (e, el,event,d) {
41002         this.fireEvent('eventclick', this, el, event);
41003     },
41004     
41005     onMonthChange: function () {
41006         this.store.load();
41007     },
41008     
41009     onLoad: function () {
41010         
41011         //Roo.log('calendar onload');
41012 //         
41013         if(this.eventStore.getCount() > 0){
41014             
41015            
41016             
41017             this.eventStore.each(function(d){
41018                 
41019                 
41020                 // FIXME..
41021                 var add =   d.data;
41022                 if (typeof(add.end_dt) == 'undefined')  {
41023                     Roo.log("Missing End time in calendar data: ");
41024                     Roo.log(d);
41025                     return;
41026                 }
41027                 if (typeof(add.start_dt) == 'undefined')  {
41028                     Roo.log("Missing Start time in calendar data: ");
41029                     Roo.log(d);
41030                     return;
41031                 }
41032                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41033                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41034                 add.id = add.id || d.id;
41035                 add.title = add.title || '??';
41036                 
41037                 this.addItem(d);
41038                 
41039              
41040             },this);
41041         }
41042         
41043         this.renderEvents();
41044     }
41045     
41046
41047 });
41048 /*
41049  grid : {
41050                 xtype: 'Grid',
41051                 xns: Roo.grid,
41052                 listeners : {
41053                     render : function ()
41054                     {
41055                         _this.grid = this;
41056                         
41057                         if (!this.view.el.hasClass('course-timesheet')) {
41058                             this.view.el.addClass('course-timesheet');
41059                         }
41060                         if (this.tsStyle) {
41061                             this.ds.load({});
41062                             return; 
41063                         }
41064                         Roo.log('width');
41065                         Roo.log(_this.grid.view.el.getWidth());
41066                         
41067                         
41068                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41069                             '.course-timesheet .x-grid-row' : {
41070                                 height: '80px'
41071                             },
41072                             '.x-grid-row td' : {
41073                                 'vertical-align' : 0
41074                             },
41075                             '.course-edit-link' : {
41076                                 'color' : 'blue',
41077                                 'text-overflow' : 'ellipsis',
41078                                 'overflow' : 'hidden',
41079                                 'white-space' : 'nowrap',
41080                                 'cursor' : 'pointer'
41081                             },
41082                             '.sub-link' : {
41083                                 'color' : 'green'
41084                             },
41085                             '.de-act-sup-link' : {
41086                                 'color' : 'purple',
41087                                 'text-decoration' : 'line-through'
41088                             },
41089                             '.de-act-link' : {
41090                                 'color' : 'red',
41091                                 'text-decoration' : 'line-through'
41092                             },
41093                             '.course-timesheet .course-highlight' : {
41094                                 'border-top-style': 'dashed !important',
41095                                 'border-bottom-bottom': 'dashed !important'
41096                             },
41097                             '.course-timesheet .course-item' : {
41098                                 'font-family'   : 'tahoma, arial, helvetica',
41099                                 'font-size'     : '11px',
41100                                 'overflow'      : 'hidden',
41101                                 'padding-left'  : '10px',
41102                                 'padding-right' : '10px',
41103                                 'padding-top' : '10px' 
41104                             }
41105                             
41106                         }, Roo.id());
41107                                 this.ds.load({});
41108                     }
41109                 },
41110                 autoWidth : true,
41111                 monitorWindowResize : false,
41112                 cellrenderer : function(v,x,r)
41113                 {
41114                     return v;
41115                 },
41116                 sm : {
41117                     xtype: 'CellSelectionModel',
41118                     xns: Roo.grid
41119                 },
41120                 dataSource : {
41121                     xtype: 'Store',
41122                     xns: Roo.data,
41123                     listeners : {
41124                         beforeload : function (_self, options)
41125                         {
41126                             options.params = options.params || {};
41127                             options.params._month = _this.monthField.getValue();
41128                             options.params.limit = 9999;
41129                             options.params['sort'] = 'when_dt';    
41130                             options.params['dir'] = 'ASC';    
41131                             this.proxy.loadResponse = this.loadResponse;
41132                             Roo.log("load?");
41133                             //this.addColumns();
41134                         },
41135                         load : function (_self, records, options)
41136                         {
41137                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41138                                 // if you click on the translation.. you can edit it...
41139                                 var el = Roo.get(this);
41140                                 var id = el.dom.getAttribute('data-id');
41141                                 var d = el.dom.getAttribute('data-date');
41142                                 var t = el.dom.getAttribute('data-time');
41143                                 //var id = this.child('span').dom.textContent;
41144                                 
41145                                 //Roo.log(this);
41146                                 Pman.Dialog.CourseCalendar.show({
41147                                     id : id,
41148                                     when_d : d,
41149                                     when_t : t,
41150                                     productitem_active : id ? 1 : 0
41151                                 }, function() {
41152                                     _this.grid.ds.load({});
41153                                 });
41154                            
41155                            });
41156                            
41157                            _this.panel.fireEvent('resize', [ '', '' ]);
41158                         }
41159                     },
41160                     loadResponse : function(o, success, response){
41161                             // this is overridden on before load..
41162                             
41163                             Roo.log("our code?");       
41164                             //Roo.log(success);
41165                             //Roo.log(response)
41166                             delete this.activeRequest;
41167                             if(!success){
41168                                 this.fireEvent("loadexception", this, o, response);
41169                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41170                                 return;
41171                             }
41172                             var result;
41173                             try {
41174                                 result = o.reader.read(response);
41175                             }catch(e){
41176                                 Roo.log("load exception?");
41177                                 this.fireEvent("loadexception", this, o, response, e);
41178                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41179                                 return;
41180                             }
41181                             Roo.log("ready...");        
41182                             // loop through result.records;
41183                             // and set this.tdate[date] = [] << array of records..
41184                             _this.tdata  = {};
41185                             Roo.each(result.records, function(r){
41186                                 //Roo.log(r.data);
41187                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41188                                     _this.tdata[r.data.when_dt.format('j')] = [];
41189                                 }
41190                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41191                             });
41192                             
41193                             //Roo.log(_this.tdata);
41194                             
41195                             result.records = [];
41196                             result.totalRecords = 6;
41197                     
41198                             // let's generate some duumy records for the rows.
41199                             //var st = _this.dateField.getValue();
41200                             
41201                             // work out monday..
41202                             //st = st.add(Date.DAY, -1 * st.format('w'));
41203                             
41204                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41205                             
41206                             var firstOfMonth = date.getFirstDayOfMonth();
41207                             var days = date.getDaysInMonth();
41208                             var d = 1;
41209                             var firstAdded = false;
41210                             for (var i = 0; i < result.totalRecords ; i++) {
41211                                 //var d= st.add(Date.DAY, i);
41212                                 var row = {};
41213                                 var added = 0;
41214                                 for(var w = 0 ; w < 7 ; w++){
41215                                     if(!firstAdded && firstOfMonth != w){
41216                                         continue;
41217                                     }
41218                                     if(d > days){
41219                                         continue;
41220                                     }
41221                                     firstAdded = true;
41222                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41223                                     row['weekday'+w] = String.format(
41224                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41225                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41226                                                     d,
41227                                                     date.format('Y-m-')+dd
41228                                                 );
41229                                     added++;
41230                                     if(typeof(_this.tdata[d]) != 'undefined'){
41231                                         Roo.each(_this.tdata[d], function(r){
41232                                             var is_sub = '';
41233                                             var deactive = '';
41234                                             var id = r.id;
41235                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41236                                             if(r.parent_id*1>0){
41237                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41238                                                 id = r.parent_id;
41239                                             }
41240                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41241                                                 deactive = 'de-act-link';
41242                                             }
41243                                             
41244                                             row['weekday'+w] += String.format(
41245                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41246                                                     id, //0
41247                                                     r.product_id_name, //1
41248                                                     r.when_dt.format('h:ia'), //2
41249                                                     is_sub, //3
41250                                                     deactive, //4
41251                                                     desc // 5
41252                                             );
41253                                         });
41254                                     }
41255                                     d++;
41256                                 }
41257                                 
41258                                 // only do this if something added..
41259                                 if(added > 0){ 
41260                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41261                                 }
41262                                 
41263                                 
41264                                 // push it twice. (second one with an hour..
41265                                 
41266                             }
41267                             //Roo.log(result);
41268                             this.fireEvent("load", this, o, o.request.arg);
41269                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41270                         },
41271                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41272                     proxy : {
41273                         xtype: 'HttpProxy',
41274                         xns: Roo.data,
41275                         method : 'GET',
41276                         url : baseURL + '/Roo/Shop_course.php'
41277                     },
41278                     reader : {
41279                         xtype: 'JsonReader',
41280                         xns: Roo.data,
41281                         id : 'id',
41282                         fields : [
41283                             {
41284                                 'name': 'id',
41285                                 'type': 'int'
41286                             },
41287                             {
41288                                 'name': 'when_dt',
41289                                 'type': 'string'
41290                             },
41291                             {
41292                                 'name': 'end_dt',
41293                                 'type': 'string'
41294                             },
41295                             {
41296                                 'name': 'parent_id',
41297                                 'type': 'int'
41298                             },
41299                             {
41300                                 'name': 'product_id',
41301                                 'type': 'int'
41302                             },
41303                             {
41304                                 'name': 'productitem_id',
41305                                 'type': 'int'
41306                             },
41307                             {
41308                                 'name': 'guid',
41309                                 'type': 'int'
41310                             }
41311                         ]
41312                     }
41313                 },
41314                 toolbar : {
41315                     xtype: 'Toolbar',
41316                     xns: Roo,
41317                     items : [
41318                         {
41319                             xtype: 'Button',
41320                             xns: Roo.Toolbar,
41321                             listeners : {
41322                                 click : function (_self, e)
41323                                 {
41324                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41325                                     sd.setMonth(sd.getMonth()-1);
41326                                     _this.monthField.setValue(sd.format('Y-m-d'));
41327                                     _this.grid.ds.load({});
41328                                 }
41329                             },
41330                             text : "Back"
41331                         },
41332                         {
41333                             xtype: 'Separator',
41334                             xns: Roo.Toolbar
41335                         },
41336                         {
41337                             xtype: 'MonthField',
41338                             xns: Roo.form,
41339                             listeners : {
41340                                 render : function (_self)
41341                                 {
41342                                     _this.monthField = _self;
41343                                    // _this.monthField.set  today
41344                                 },
41345                                 select : function (combo, date)
41346                                 {
41347                                     _this.grid.ds.load({});
41348                                 }
41349                             },
41350                             value : (function() { return new Date(); })()
41351                         },
41352                         {
41353                             xtype: 'Separator',
41354                             xns: Roo.Toolbar
41355                         },
41356                         {
41357                             xtype: 'TextItem',
41358                             xns: Roo.Toolbar,
41359                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41360                         },
41361                         {
41362                             xtype: 'Fill',
41363                             xns: Roo.Toolbar
41364                         },
41365                         {
41366                             xtype: 'Button',
41367                             xns: Roo.Toolbar,
41368                             listeners : {
41369                                 click : function (_self, e)
41370                                 {
41371                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41372                                     sd.setMonth(sd.getMonth()+1);
41373                                     _this.monthField.setValue(sd.format('Y-m-d'));
41374                                     _this.grid.ds.load({});
41375                                 }
41376                             },
41377                             text : "Next"
41378                         }
41379                     ]
41380                 },
41381                  
41382             }
41383         };
41384         
41385         *//*
41386  * Based on:
41387  * Ext JS Library 1.1.1
41388  * Copyright(c) 2006-2007, Ext JS, LLC.
41389  *
41390  * Originally Released Under LGPL - original licence link has changed is not relivant.
41391  *
41392  * Fork - LGPL
41393  * <script type="text/javascript">
41394  */
41395  
41396 /**
41397  * @class Roo.LoadMask
41398  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41399  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41400  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41401  * element's UpdateManager load indicator and will be destroyed after the initial load.
41402  * @constructor
41403  * Create a new LoadMask
41404  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41405  * @param {Object} config The config object
41406  */
41407 Roo.LoadMask = function(el, config){
41408     this.el = Roo.get(el);
41409     Roo.apply(this, config);
41410     if(this.store){
41411         this.store.on('beforeload', this.onBeforeLoad, this);
41412         this.store.on('load', this.onLoad, this);
41413         this.store.on('loadexception', this.onLoadException, this);
41414         this.removeMask = false;
41415     }else{
41416         var um = this.el.getUpdateManager();
41417         um.showLoadIndicator = false; // disable the default indicator
41418         um.on('beforeupdate', this.onBeforeLoad, this);
41419         um.on('update', this.onLoad, this);
41420         um.on('failure', this.onLoad, this);
41421         this.removeMask = true;
41422     }
41423 };
41424
41425 Roo.LoadMask.prototype = {
41426     /**
41427      * @cfg {Boolean} removeMask
41428      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41429      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41430      */
41431     /**
41432      * @cfg {String} msg
41433      * The text to display in a centered loading message box (defaults to 'Loading...')
41434      */
41435     msg : 'Loading...',
41436     /**
41437      * @cfg {String} msgCls
41438      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41439      */
41440     msgCls : 'x-mask-loading',
41441
41442     /**
41443      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41444      * @type Boolean
41445      */
41446     disabled: false,
41447
41448     /**
41449      * Disables the mask to prevent it from being displayed
41450      */
41451     disable : function(){
41452        this.disabled = true;
41453     },
41454
41455     /**
41456      * Enables the mask so that it can be displayed
41457      */
41458     enable : function(){
41459         this.disabled = false;
41460     },
41461     
41462     onLoadException : function()
41463     {
41464         Roo.log(arguments);
41465         
41466         if (typeof(arguments[3]) != 'undefined') {
41467             Roo.MessageBox.alert("Error loading",arguments[3]);
41468         } 
41469         /*
41470         try {
41471             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41472                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41473             }   
41474         } catch(e) {
41475             
41476         }
41477         */
41478     
41479         
41480         
41481         this.el.unmask(this.removeMask);
41482     },
41483     // private
41484     onLoad : function()
41485     {
41486         this.el.unmask(this.removeMask);
41487     },
41488
41489     // private
41490     onBeforeLoad : function(){
41491         if(!this.disabled){
41492             this.el.mask(this.msg, this.msgCls);
41493         }
41494     },
41495
41496     // private
41497     destroy : function(){
41498         if(this.store){
41499             this.store.un('beforeload', this.onBeforeLoad, this);
41500             this.store.un('load', this.onLoad, this);
41501             this.store.un('loadexception', this.onLoadException, this);
41502         }else{
41503             var um = this.el.getUpdateManager();
41504             um.un('beforeupdate', this.onBeforeLoad, this);
41505             um.un('update', this.onLoad, this);
41506             um.un('failure', this.onLoad, this);
41507         }
41508     }
41509 };/*
41510  * Based on:
41511  * Ext JS Library 1.1.1
41512  * Copyright(c) 2006-2007, Ext JS, LLC.
41513  *
41514  * Originally Released Under LGPL - original licence link has changed is not relivant.
41515  *
41516  * Fork - LGPL
41517  * <script type="text/javascript">
41518  */
41519
41520
41521 /**
41522  * @class Roo.XTemplate
41523  * @extends Roo.Template
41524  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
41525 <pre><code>
41526 var t = new Roo.XTemplate(
41527         '&lt;select name="{name}"&gt;',
41528                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
41529         '&lt;/select&gt;'
41530 );
41531  
41532 // then append, applying the master template values
41533  </code></pre>
41534  *
41535  * Supported features:
41536  *
41537  *  Tags:
41538
41539 <pre><code>
41540       {a_variable} - output encoded.
41541       {a_variable.format:("Y-m-d")} - call a method on the variable
41542       {a_variable:raw} - unencoded output
41543       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
41544       {a_variable:this.method_on_template(...)} - call a method on the template object.
41545  
41546 </code></pre>
41547  *  The tpl tag:
41548 <pre><code>
41549         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
41550         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
41551         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
41552         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
41553   
41554         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
41555         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
41556 </code></pre>
41557  *      
41558  */
41559 Roo.XTemplate = function()
41560 {
41561     Roo.XTemplate.superclass.constructor.apply(this, arguments);
41562     if (this.html) {
41563         this.compile();
41564     }
41565 };
41566
41567
41568 Roo.extend(Roo.XTemplate, Roo.Template, {
41569
41570     /**
41571      * The various sub templates
41572      */
41573     tpls : false,
41574     /**
41575      *
41576      * basic tag replacing syntax
41577      * WORD:WORD()
41578      *
41579      * // you can fake an object call by doing this
41580      *  x.t:(test,tesT) 
41581      * 
41582      */
41583     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
41584
41585     /**
41586      * compile the template
41587      *
41588      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
41589      *
41590      */
41591     compile: function()
41592     {
41593         var s = this.html;
41594      
41595         s = ['<tpl>', s, '</tpl>'].join('');
41596     
41597         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
41598             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
41599             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
41600             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
41601             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
41602             m,
41603             id     = 0,
41604             tpls   = [];
41605     
41606         while(true == !!(m = s.match(re))){
41607             var forMatch   = m[0].match(nameRe),
41608                 ifMatch   = m[0].match(ifRe),
41609                 execMatch   = m[0].match(execRe),
41610                 namedMatch   = m[0].match(namedRe),
41611                 
41612                 exp  = null, 
41613                 fn   = null,
41614                 exec = null,
41615                 name = forMatch && forMatch[1] ? forMatch[1] : '';
41616                 
41617             if (ifMatch) {
41618                 // if - puts fn into test..
41619                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
41620                 if(exp){
41621                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
41622                 }
41623             }
41624             
41625             if (execMatch) {
41626                 // exec - calls a function... returns empty if true is  returned.
41627                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
41628                 if(exp){
41629                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
41630                 }
41631             }
41632             
41633             
41634             if (name) {
41635                 // for = 
41636                 switch(name){
41637                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
41638                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
41639                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
41640                 }
41641             }
41642             var uid = namedMatch ? namedMatch[1] : id;
41643             
41644             
41645             tpls.push({
41646                 id:     namedMatch ? namedMatch[1] : id,
41647                 target: name,
41648                 exec:   exec,
41649                 test:   fn,
41650                 body:   m[1] || ''
41651             });
41652             if (namedMatch) {
41653                 s = s.replace(m[0], '');
41654             } else { 
41655                 s = s.replace(m[0], '{xtpl'+ id + '}');
41656             }
41657             ++id;
41658         }
41659         this.tpls = [];
41660         for(var i = tpls.length-1; i >= 0; --i){
41661             this.compileTpl(tpls[i]);
41662             this.tpls[tpls[i].id] = tpls[i];
41663         }
41664         this.master = tpls[tpls.length-1];
41665         return this;
41666     },
41667     /**
41668      * same as applyTemplate, except it's done to one of the subTemplates
41669      * when using named templates, you can do:
41670      *
41671      * var str = pl.applySubTemplate('your-name', values);
41672      *
41673      * 
41674      * @param {Number} id of the template
41675      * @param {Object} values to apply to template
41676      * @param {Object} parent (normaly the instance of this object)
41677      */
41678     applySubTemplate : function(id, values, parent)
41679     {
41680         
41681         
41682         var t = this.tpls[id];
41683         
41684         
41685         try { 
41686             if(t.test && !t.test.call(this, values, parent)){
41687                 return '';
41688             }
41689         } catch(e) {
41690             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
41691             Roo.log(e.toString());
41692             Roo.log(t.test);
41693             return ''
41694         }
41695         try { 
41696             
41697             if(t.exec && t.exec.call(this, values, parent)){
41698                 return '';
41699             }
41700         } catch(e) {
41701             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
41702             Roo.log(e.toString());
41703             Roo.log(t.exec);
41704             return ''
41705         }
41706         try {
41707             var vs = t.target ? t.target.call(this, values, parent) : values;
41708             parent = t.target ? values : parent;
41709             if(t.target && vs instanceof Array){
41710                 var buf = [];
41711                 for(var i = 0, len = vs.length; i < len; i++){
41712                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
41713                 }
41714                 return buf.join('');
41715             }
41716             return t.compiled.call(this, vs, parent);
41717         } catch (e) {
41718             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
41719             Roo.log(e.toString());
41720             Roo.log(t.compiled);
41721             return '';
41722         }
41723     },
41724
41725     compileTpl : function(tpl)
41726     {
41727         var fm = Roo.util.Format;
41728         var useF = this.disableFormats !== true;
41729         var sep = Roo.isGecko ? "+" : ",";
41730         var undef = function(str) {
41731             Roo.log("Property not found :"  + str);
41732             return '';
41733         };
41734         
41735         var fn = function(m, name, format, args)
41736         {
41737             //Roo.log(arguments);
41738             args = args ? args.replace(/\\'/g,"'") : args;
41739             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
41740             if (typeof(format) == 'undefined') {
41741                 format= 'htmlEncode';
41742             }
41743             if (format == 'raw' ) {
41744                 format = false;
41745             }
41746             
41747             if(name.substr(0, 4) == 'xtpl'){
41748                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
41749             }
41750             
41751             // build an array of options to determine if value is undefined..
41752             
41753             // basically get 'xxxx.yyyy' then do
41754             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
41755             //    (function () { Roo.log("Property not found"); return ''; })() :
41756             //    ......
41757             
41758             var udef_ar = [];
41759             var lookfor = '';
41760             Roo.each(name.split('.'), function(st) {
41761                 lookfor += (lookfor.length ? '.': '') + st;
41762                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
41763             });
41764             
41765             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
41766             
41767             
41768             if(format && useF){
41769                 
41770                 args = args ? ',' + args : "";
41771                  
41772                 if(format.substr(0, 5) != "this."){
41773                     format = "fm." + format + '(';
41774                 }else{
41775                     format = 'this.call("'+ format.substr(5) + '", ';
41776                     args = ", values";
41777                 }
41778                 
41779                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
41780             }
41781              
41782             if (args.length) {
41783                 // called with xxyx.yuu:(test,test)
41784                 // change to ()
41785                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
41786             }
41787             // raw.. - :raw modifier..
41788             return "'"+ sep + udef_st  + name + ")"+sep+"'";
41789             
41790         };
41791         var body;
41792         // branched to use + in gecko and [].join() in others
41793         if(Roo.isGecko){
41794             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
41795                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
41796                     "';};};";
41797         }else{
41798             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
41799             body.push(tpl.body.replace(/(\r\n|\n)/g,
41800                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
41801             body.push("'].join('');};};");
41802             body = body.join('');
41803         }
41804         
41805         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
41806        
41807         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
41808         eval(body);
41809         
41810         return this;
41811     },
41812
41813     applyTemplate : function(values){
41814         return this.master.compiled.call(this, values, {});
41815         //var s = this.subs;
41816     },
41817
41818     apply : function(){
41819         return this.applyTemplate.apply(this, arguments);
41820     }
41821
41822  });
41823
41824 Roo.XTemplate.from = function(el){
41825     el = Roo.getDom(el);
41826     return new Roo.XTemplate(el.value || el.innerHTML);
41827 };