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         ss['background-attachment'] = 'fixed'; // w3c
24983         dbody.bgProperties = 'fixed'; // ie
24984         Roo.DomHelper.applyStyles(dbody, ss);
24985         Roo.EventManager.on(this.doc, {
24986             //'mousedown': this.onEditorEvent,
24987             'mouseup': this.onEditorEvent,
24988             'dblclick': this.onEditorEvent,
24989             'click': this.onEditorEvent,
24990             'keyup': this.onEditorEvent,
24991             buffer:100,
24992             scope: this
24993         });
24994         if(Roo.isGecko){
24995             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24996         }
24997         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24998             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24999         }
25000         this.initialized = true;
25001
25002         this.owner.fireEvent('initialize', this);
25003         this.pushValue();
25004     },
25005
25006     // private
25007     onDestroy : function(){
25008         
25009         
25010         
25011         if(this.rendered){
25012             
25013             //for (var i =0; i < this.toolbars.length;i++) {
25014             //    // fixme - ask toolbars for heights?
25015             //    this.toolbars[i].onDestroy();
25016            // }
25017             
25018             //this.wrap.dom.innerHTML = '';
25019             //this.wrap.remove();
25020         }
25021     },
25022
25023     // private
25024     onFirstFocus : function(){
25025         
25026         this.assignDocWin();
25027         
25028         
25029         this.activated = true;
25030          
25031     
25032         if(Roo.isGecko){ // prevent silly gecko errors
25033             this.win.focus();
25034             var s = this.win.getSelection();
25035             if(!s.focusNode || s.focusNode.nodeType != 3){
25036                 var r = s.getRangeAt(0);
25037                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25038                 r.collapse(true);
25039                 this.deferFocus();
25040             }
25041             try{
25042                 this.execCmd('useCSS', true);
25043                 this.execCmd('styleWithCSS', false);
25044             }catch(e){}
25045         }
25046         this.owner.fireEvent('activate', this);
25047     },
25048
25049     // private
25050     adjustFont: function(btn){
25051         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25052         //if(Roo.isSafari){ // safari
25053         //    adjust *= 2;
25054        // }
25055         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25056         if(Roo.isSafari){ // safari
25057             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25058             v =  (v < 10) ? 10 : v;
25059             v =  (v > 48) ? 48 : v;
25060             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25061             
25062         }
25063         
25064         
25065         v = Math.max(1, v+adjust);
25066         
25067         this.execCmd('FontSize', v  );
25068     },
25069
25070     onEditorEvent : function(e){
25071         this.owner.fireEvent('editorevent', this, e);
25072       //  this.updateToolbar();
25073         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25074     },
25075
25076     insertTag : function(tg)
25077     {
25078         // could be a bit smarter... -> wrap the current selected tRoo..
25079         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25080             
25081             range = this.createRange(this.getSelection());
25082             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25083             wrappingNode.appendChild(range.extractContents());
25084             range.insertNode(wrappingNode);
25085
25086             return;
25087             
25088             
25089             
25090         }
25091         this.execCmd("formatblock",   tg);
25092         
25093     },
25094     
25095     insertText : function(txt)
25096     {
25097         
25098         
25099         var range = this.createRange();
25100         range.deleteContents();
25101                //alert(Sender.getAttribute('label'));
25102                
25103         range.insertNode(this.doc.createTextNode(txt));
25104     } ,
25105     
25106      
25107
25108     /**
25109      * Executes a Midas editor command on the editor document and performs necessary focus and
25110      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25111      * @param {String} cmd The Midas command
25112      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25113      */
25114     relayCmd : function(cmd, value){
25115         this.win.focus();
25116         this.execCmd(cmd, value);
25117         this.owner.fireEvent('editorevent', this);
25118         //this.updateToolbar();
25119         this.owner.deferFocus();
25120     },
25121
25122     /**
25123      * Executes a Midas editor command directly on the editor document.
25124      * For visual commands, you should use {@link #relayCmd} instead.
25125      * <b>This should only be called after the editor is initialized.</b>
25126      * @param {String} cmd The Midas command
25127      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25128      */
25129     execCmd : function(cmd, value){
25130         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25131         this.syncValue();
25132     },
25133  
25134  
25135    
25136     /**
25137      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25138      * to insert tRoo.
25139      * @param {String} text | dom node.. 
25140      */
25141     insertAtCursor : function(text)
25142     {
25143         
25144         
25145         
25146         if(!this.activated){
25147             return;
25148         }
25149         /*
25150         if(Roo.isIE){
25151             this.win.focus();
25152             var r = this.doc.selection.createRange();
25153             if(r){
25154                 r.collapse(true);
25155                 r.pasteHTML(text);
25156                 this.syncValue();
25157                 this.deferFocus();
25158             
25159             }
25160             return;
25161         }
25162         */
25163         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25164             this.win.focus();
25165             
25166             
25167             // from jquery ui (MIT licenced)
25168             var range, node;
25169             var win = this.win;
25170             
25171             if (win.getSelection && win.getSelection().getRangeAt) {
25172                 range = win.getSelection().getRangeAt(0);
25173                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25174                 range.insertNode(node);
25175             } else if (win.document.selection && win.document.selection.createRange) {
25176                 // no firefox support
25177                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25178                 win.document.selection.createRange().pasteHTML(txt);
25179             } else {
25180                 // no firefox support
25181                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25182                 this.execCmd('InsertHTML', txt);
25183             } 
25184             
25185             this.syncValue();
25186             
25187             this.deferFocus();
25188         }
25189     },
25190  // private
25191     mozKeyPress : function(e){
25192         if(e.ctrlKey){
25193             var c = e.getCharCode(), cmd;
25194           
25195             if(c > 0){
25196                 c = String.fromCharCode(c).toLowerCase();
25197                 switch(c){
25198                     case 'b':
25199                         cmd = 'bold';
25200                         break;
25201                     case 'i':
25202                         cmd = 'italic';
25203                         break;
25204                     
25205                     case 'u':
25206                         cmd = 'underline';
25207                         break;
25208                     
25209                     case 'v':
25210                         this.cleanUpPaste.defer(100, this);
25211                         return;
25212                         
25213                 }
25214                 if(cmd){
25215                     this.win.focus();
25216                     this.execCmd(cmd);
25217                     this.deferFocus();
25218                     e.preventDefault();
25219                 }
25220                 
25221             }
25222         }
25223     },
25224
25225     // private
25226     fixKeys : function(){ // load time branching for fastest keydown performance
25227         if(Roo.isIE){
25228             return function(e){
25229                 var k = e.getKey(), r;
25230                 if(k == e.TAB){
25231                     e.stopEvent();
25232                     r = this.doc.selection.createRange();
25233                     if(r){
25234                         r.collapse(true);
25235                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25236                         this.deferFocus();
25237                     }
25238                     return;
25239                 }
25240                 
25241                 if(k == e.ENTER){
25242                     r = this.doc.selection.createRange();
25243                     if(r){
25244                         var target = r.parentElement();
25245                         if(!target || target.tagName.toLowerCase() != 'li'){
25246                             e.stopEvent();
25247                             r.pasteHTML('<br />');
25248                             r.collapse(false);
25249                             r.select();
25250                         }
25251                     }
25252                 }
25253                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25254                     this.cleanUpPaste.defer(100, this);
25255                     return;
25256                 }
25257                 
25258                 
25259             };
25260         }else if(Roo.isOpera){
25261             return function(e){
25262                 var k = e.getKey();
25263                 if(k == e.TAB){
25264                     e.stopEvent();
25265                     this.win.focus();
25266                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25267                     this.deferFocus();
25268                 }
25269                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25270                     this.cleanUpPaste.defer(100, this);
25271                     return;
25272                 }
25273                 
25274             };
25275         }else if(Roo.isSafari){
25276             return function(e){
25277                 var k = e.getKey();
25278                 
25279                 if(k == e.TAB){
25280                     e.stopEvent();
25281                     this.execCmd('InsertText','\t');
25282                     this.deferFocus();
25283                     return;
25284                 }
25285                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25286                     this.cleanUpPaste.defer(100, this);
25287                     return;
25288                 }
25289                 
25290              };
25291         }
25292     }(),
25293     
25294     getAllAncestors: function()
25295     {
25296         var p = this.getSelectedNode();
25297         var a = [];
25298         if (!p) {
25299             a.push(p); // push blank onto stack..
25300             p = this.getParentElement();
25301         }
25302         
25303         
25304         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25305             a.push(p);
25306             p = p.parentNode;
25307         }
25308         a.push(this.doc.body);
25309         return a;
25310     },
25311     lastSel : false,
25312     lastSelNode : false,
25313     
25314     
25315     getSelection : function() 
25316     {
25317         this.assignDocWin();
25318         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25319     },
25320     
25321     getSelectedNode: function() 
25322     {
25323         // this may only work on Gecko!!!
25324         
25325         // should we cache this!!!!
25326         
25327         
25328         
25329          
25330         var range = this.createRange(this.getSelection()).cloneRange();
25331         
25332         if (Roo.isIE) {
25333             var parent = range.parentElement();
25334             while (true) {
25335                 var testRange = range.duplicate();
25336                 testRange.moveToElementText(parent);
25337                 if (testRange.inRange(range)) {
25338                     break;
25339                 }
25340                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25341                     break;
25342                 }
25343                 parent = parent.parentElement;
25344             }
25345             return parent;
25346         }
25347         
25348         // is ancestor a text element.
25349         var ac =  range.commonAncestorContainer;
25350         if (ac.nodeType == 3) {
25351             ac = ac.parentNode;
25352         }
25353         
25354         var ar = ac.childNodes;
25355          
25356         var nodes = [];
25357         var other_nodes = [];
25358         var has_other_nodes = false;
25359         for (var i=0;i<ar.length;i++) {
25360             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25361                 continue;
25362             }
25363             // fullly contained node.
25364             
25365             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25366                 nodes.push(ar[i]);
25367                 continue;
25368             }
25369             
25370             // probably selected..
25371             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25372                 other_nodes.push(ar[i]);
25373                 continue;
25374             }
25375             // outer..
25376             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25377                 continue;
25378             }
25379             
25380             
25381             has_other_nodes = true;
25382         }
25383         if (!nodes.length && other_nodes.length) {
25384             nodes= other_nodes;
25385         }
25386         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25387             return false;
25388         }
25389         
25390         return nodes[0];
25391     },
25392     createRange: function(sel)
25393     {
25394         // this has strange effects when using with 
25395         // top toolbar - not sure if it's a great idea.
25396         //this.editor.contentWindow.focus();
25397         if (typeof sel != "undefined") {
25398             try {
25399                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25400             } catch(e) {
25401                 return this.doc.createRange();
25402             }
25403         } else {
25404             return this.doc.createRange();
25405         }
25406     },
25407     getParentElement: function()
25408     {
25409         
25410         this.assignDocWin();
25411         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25412         
25413         var range = this.createRange(sel);
25414          
25415         try {
25416             var p = range.commonAncestorContainer;
25417             while (p.nodeType == 3) { // text node
25418                 p = p.parentNode;
25419             }
25420             return p;
25421         } catch (e) {
25422             return null;
25423         }
25424     
25425     },
25426     /***
25427      *
25428      * Range intersection.. the hard stuff...
25429      *  '-1' = before
25430      *  '0' = hits..
25431      *  '1' = after.
25432      *         [ -- selected range --- ]
25433      *   [fail]                        [fail]
25434      *
25435      *    basically..
25436      *      if end is before start or  hits it. fail.
25437      *      if start is after end or hits it fail.
25438      *
25439      *   if either hits (but other is outside. - then it's not 
25440      *   
25441      *    
25442      **/
25443     
25444     
25445     // @see http://www.thismuchiknow.co.uk/?p=64.
25446     rangeIntersectsNode : function(range, node)
25447     {
25448         var nodeRange = node.ownerDocument.createRange();
25449         try {
25450             nodeRange.selectNode(node);
25451         } catch (e) {
25452             nodeRange.selectNodeContents(node);
25453         }
25454     
25455         var rangeStartRange = range.cloneRange();
25456         rangeStartRange.collapse(true);
25457     
25458         var rangeEndRange = range.cloneRange();
25459         rangeEndRange.collapse(false);
25460     
25461         var nodeStartRange = nodeRange.cloneRange();
25462         nodeStartRange.collapse(true);
25463     
25464         var nodeEndRange = nodeRange.cloneRange();
25465         nodeEndRange.collapse(false);
25466     
25467         return rangeStartRange.compareBoundaryPoints(
25468                  Range.START_TO_START, nodeEndRange) == -1 &&
25469                rangeEndRange.compareBoundaryPoints(
25470                  Range.START_TO_START, nodeStartRange) == 1;
25471         
25472          
25473     },
25474     rangeCompareNode : function(range, node)
25475     {
25476         var nodeRange = node.ownerDocument.createRange();
25477         try {
25478             nodeRange.selectNode(node);
25479         } catch (e) {
25480             nodeRange.selectNodeContents(node);
25481         }
25482         
25483         
25484         range.collapse(true);
25485     
25486         nodeRange.collapse(true);
25487      
25488         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25489         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25490          
25491         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25492         
25493         var nodeIsBefore   =  ss == 1;
25494         var nodeIsAfter    = ee == -1;
25495         
25496         if (nodeIsBefore && nodeIsAfter)
25497             return 0; // outer
25498         if (!nodeIsBefore && nodeIsAfter)
25499             return 1; //right trailed.
25500         
25501         if (nodeIsBefore && !nodeIsAfter)
25502             return 2;  // left trailed.
25503         // fully contined.
25504         return 3;
25505     },
25506
25507     // private? - in a new class?
25508     cleanUpPaste :  function()
25509     {
25510         // cleans up the whole document..
25511         Roo.log('cleanuppaste');
25512         
25513         this.cleanUpChildren(this.doc.body);
25514         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25515         if (clean != this.doc.body.innerHTML) {
25516             this.doc.body.innerHTML = clean;
25517         }
25518         
25519     },
25520     
25521     cleanWordChars : function(input) {// change the chars to hex code
25522         var he = Roo.HtmlEditorCore;
25523         
25524         var output = input;
25525         Roo.each(he.swapCodes, function(sw) { 
25526             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25527             
25528             output = output.replace(swapper, sw[1]);
25529         });
25530         
25531         return output;
25532     },
25533     
25534     
25535     cleanUpChildren : function (n)
25536     {
25537         if (!n.childNodes.length) {
25538             return;
25539         }
25540         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25541            this.cleanUpChild(n.childNodes[i]);
25542         }
25543     },
25544     
25545     
25546         
25547     
25548     cleanUpChild : function (node)
25549     {
25550         var ed = this;
25551         //console.log(node);
25552         if (node.nodeName == "#text") {
25553             // clean up silly Windows -- stuff?
25554             return; 
25555         }
25556         if (node.nodeName == "#comment") {
25557             node.parentNode.removeChild(node);
25558             // clean up silly Windows -- stuff?
25559             return; 
25560         }
25561         
25562         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
25563             // remove node.
25564             node.parentNode.removeChild(node);
25565             return;
25566             
25567         }
25568         
25569         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25570         
25571         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25572         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25573         
25574         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25575         //    remove_keep_children = true;
25576         //}
25577         
25578         if (remove_keep_children) {
25579             this.cleanUpChildren(node);
25580             // inserts everything just before this node...
25581             while (node.childNodes.length) {
25582                 var cn = node.childNodes[0];
25583                 node.removeChild(cn);
25584                 node.parentNode.insertBefore(cn, node);
25585             }
25586             node.parentNode.removeChild(node);
25587             return;
25588         }
25589         
25590         if (!node.attributes || !node.attributes.length) {
25591             this.cleanUpChildren(node);
25592             return;
25593         }
25594         
25595         function cleanAttr(n,v)
25596         {
25597             
25598             if (v.match(/^\./) || v.match(/^\//)) {
25599                 return;
25600             }
25601             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25602                 return;
25603             }
25604             if (v.match(/^#/)) {
25605                 return;
25606             }
25607 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25608             node.removeAttribute(n);
25609             
25610         }
25611         
25612         function cleanStyle(n,v)
25613         {
25614             if (v.match(/expression/)) { //XSS?? should we even bother..
25615                 node.removeAttribute(n);
25616                 return;
25617             }
25618             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
25619             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
25620             
25621             
25622             var parts = v.split(/;/);
25623             var clean = [];
25624             
25625             Roo.each(parts, function(p) {
25626                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25627                 if (!p.length) {
25628                     return true;
25629                 }
25630                 var l = p.split(':').shift().replace(/\s+/g,'');
25631                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25632                 
25633                 if ( cblack.indexOf(l) > -1) {
25634 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25635                     //node.removeAttribute(n);
25636                     return true;
25637                 }
25638                 //Roo.log()
25639                 // only allow 'c whitelisted system attributes'
25640                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25641 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25642                     //node.removeAttribute(n);
25643                     return true;
25644                 }
25645                 
25646                 
25647                  
25648                 
25649                 clean.push(p);
25650                 return true;
25651             });
25652             if (clean.length) { 
25653                 node.setAttribute(n, clean.join(';'));
25654             } else {
25655                 node.removeAttribute(n);
25656             }
25657             
25658         }
25659         
25660         
25661         for (var i = node.attributes.length-1; i > -1 ; i--) {
25662             var a = node.attributes[i];
25663             //console.log(a);
25664             
25665             if (a.name.toLowerCase().substr(0,2)=='on')  {
25666                 node.removeAttribute(a.name);
25667                 continue;
25668             }
25669             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25670                 node.removeAttribute(a.name);
25671                 continue;
25672             }
25673             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25674                 cleanAttr(a.name,a.value); // fixme..
25675                 continue;
25676             }
25677             if (a.name == 'style') {
25678                 cleanStyle(a.name,a.value);
25679                 continue;
25680             }
25681             /// clean up MS crap..
25682             // tecnically this should be a list of valid class'es..
25683             
25684             
25685             if (a.name == 'class') {
25686                 if (a.value.match(/^Mso/)) {
25687                     node.className = '';
25688                 }
25689                 
25690                 if (a.value.match(/body/)) {
25691                     node.className = '';
25692                 }
25693                 continue;
25694             }
25695             
25696             // style cleanup!?
25697             // class cleanup?
25698             
25699         }
25700         
25701         
25702         this.cleanUpChildren(node);
25703         
25704         
25705     },
25706     /**
25707      * Clean up MS wordisms...
25708      */
25709     cleanWord : function(node)
25710     {
25711         var _t = this;
25712         var cleanWordChildren = function()
25713         {
25714             if (!node.childNodes.length) {
25715                 return;
25716             }
25717             for (var i = node.childNodes.length-1; i > -1 ; i--) {
25718                _t.cleanWord(node.childNodes[i]);
25719             }
25720         }
25721         
25722         
25723         if (!node) {
25724             this.cleanWord(this.doc.body);
25725             return;
25726         }
25727         if (node.nodeName == "#text") {
25728             // clean up silly Windows -- stuff?
25729             return; 
25730         }
25731         if (node.nodeName == "#comment") {
25732             node.parentNode.removeChild(node);
25733             // clean up silly Windows -- stuff?
25734             return; 
25735         }
25736         
25737         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25738             node.parentNode.removeChild(node);
25739             return;
25740         }
25741         
25742         // remove - but keep children..
25743         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25744             while (node.childNodes.length) {
25745                 var cn = node.childNodes[0];
25746                 node.removeChild(cn);
25747                 node.parentNode.insertBefore(cn, node);
25748             }
25749             node.parentNode.removeChild(node);
25750             cleanWordChildren();
25751             return;
25752         }
25753         // clean styles
25754         if (node.className.length) {
25755             
25756             var cn = node.className.split(/\W+/);
25757             var cna = [];
25758             Roo.each(cn, function(cls) {
25759                 if (cls.match(/Mso[a-zA-Z]+/)) {
25760                     return;
25761                 }
25762                 cna.push(cls);
25763             });
25764             node.className = cna.length ? cna.join(' ') : '';
25765             if (!cna.length) {
25766                 node.removeAttribute("class");
25767             }
25768         }
25769         
25770         if (node.hasAttribute("lang")) {
25771             node.removeAttribute("lang");
25772         }
25773         
25774         if (node.hasAttribute("style")) {
25775             
25776             var styles = node.getAttribute("style").split(";");
25777             var nstyle = [];
25778             Roo.each(styles, function(s) {
25779                 if (!s.match(/:/)) {
25780                     return;
25781                 }
25782                 var kv = s.split(":");
25783                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25784                     return;
25785                 }
25786                 // what ever is left... we allow.
25787                 nstyle.push(s);
25788             });
25789             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25790             if (!nstyle.length) {
25791                 node.removeAttribute('style');
25792             }
25793         }
25794         
25795         cleanWordChildren();
25796         
25797         
25798     },
25799     domToHTML : function(currentElement, depth, nopadtext) {
25800         
25801             depth = depth || 0;
25802             nopadtext = nopadtext || false;
25803         
25804             if (!currentElement) {
25805                 return this.domToHTML(this.doc.body);
25806             }
25807             
25808             //Roo.log(currentElement);
25809             var j;
25810             var allText = false;
25811             var nodeName = currentElement.nodeName;
25812             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25813             
25814             if  (nodeName == '#text') {
25815                 return currentElement.nodeValue;
25816             }
25817             
25818             
25819             var ret = '';
25820             if (nodeName != 'BODY') {
25821                  
25822                 var i = 0;
25823                 // Prints the node tagName, such as <A>, <IMG>, etc
25824                 if (tagName) {
25825                     var attr = [];
25826                     for(i = 0; i < currentElement.attributes.length;i++) {
25827                         // quoting?
25828                         var aname = currentElement.attributes.item(i).name;
25829                         if (!currentElement.attributes.item(i).value.length) {
25830                             continue;
25831                         }
25832                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25833                     }
25834                     
25835                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25836                 } 
25837                 else {
25838                     
25839                     // eack
25840                 }
25841             } else {
25842                 tagName = false;
25843             }
25844             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25845                 return ret;
25846             }
25847             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25848                 nopadtext = true;
25849             }
25850             
25851             
25852             // Traverse the tree
25853             i = 0;
25854             var currentElementChild = currentElement.childNodes.item(i);
25855             var allText = true;
25856             var innerHTML  = '';
25857             lastnode = '';
25858             while (currentElementChild) {
25859                 // Formatting code (indent the tree so it looks nice on the screen)
25860                 var nopad = nopadtext;
25861                 if (lastnode == 'SPAN') {
25862                     nopad  = true;
25863                 }
25864                 // text
25865                 if  (currentElementChild.nodeName == '#text') {
25866                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25867                     if (!nopad && toadd.length > 80) {
25868                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25869                     }
25870                     innerHTML  += toadd;
25871                     
25872                     i++;
25873                     currentElementChild = currentElement.childNodes.item(i);
25874                     lastNode = '';
25875                     continue;
25876                 }
25877                 allText = false;
25878                 
25879                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25880                     
25881                 // Recursively traverse the tree structure of the child node
25882                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25883                 lastnode = currentElementChild.nodeName;
25884                 i++;
25885                 currentElementChild=currentElement.childNodes.item(i);
25886             }
25887             
25888             ret += innerHTML;
25889             
25890             if (!allText) {
25891                     // The remaining code is mostly for formatting the tree
25892                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25893             }
25894             
25895             
25896             if (tagName) {
25897                 ret+= "</"+tagName+">";
25898             }
25899             return ret;
25900             
25901         }
25902     
25903     // hide stuff that is not compatible
25904     /**
25905      * @event blur
25906      * @hide
25907      */
25908     /**
25909      * @event change
25910      * @hide
25911      */
25912     /**
25913      * @event focus
25914      * @hide
25915      */
25916     /**
25917      * @event specialkey
25918      * @hide
25919      */
25920     /**
25921      * @cfg {String} fieldClass @hide
25922      */
25923     /**
25924      * @cfg {String} focusClass @hide
25925      */
25926     /**
25927      * @cfg {String} autoCreate @hide
25928      */
25929     /**
25930      * @cfg {String} inputType @hide
25931      */
25932     /**
25933      * @cfg {String} invalidClass @hide
25934      */
25935     /**
25936      * @cfg {String} invalidText @hide
25937      */
25938     /**
25939      * @cfg {String} msgFx @hide
25940      */
25941     /**
25942      * @cfg {String} validateOnBlur @hide
25943      */
25944 });
25945
25946 Roo.HtmlEditorCore.white = [
25947         'area', 'br', 'img', 'input', 'hr', 'wbr',
25948         
25949        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25950        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25951        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25952        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25953        'table',   'ul',         'xmp', 
25954        
25955        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25956       'thead',   'tr', 
25957      
25958       'dir', 'menu', 'ol', 'ul', 'dl',
25959        
25960       'embed',  'object'
25961 ];
25962
25963
25964 Roo.HtmlEditorCore.black = [
25965     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25966         'applet', // 
25967         'base',   'basefont', 'bgsound', 'blink',  'body', 
25968         'frame',  'frameset', 'head',    'html',   'ilayer', 
25969         'iframe', 'layer',  'link',     'meta',    'object',   
25970         'script', 'style' ,'title',  'xml' // clean later..
25971 ];
25972 Roo.HtmlEditorCore.clean = [
25973     'script', 'style', 'title', 'xml'
25974 ];
25975 Roo.HtmlEditorCore.remove = [
25976     'font'
25977 ];
25978 // attributes..
25979
25980 Roo.HtmlEditorCore.ablack = [
25981     'on'
25982 ];
25983     
25984 Roo.HtmlEditorCore.aclean = [ 
25985     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25986 ];
25987
25988 // protocols..
25989 Roo.HtmlEditorCore.pwhite= [
25990         'http',  'https',  'mailto'
25991 ];
25992
25993 // white listed style attributes.
25994 Roo.HtmlEditorCore.cwhite= [
25995       //  'text-align', /// default is to allow most things..
25996       
25997          
25998 //        'font-size'//??
25999 ];
26000
26001 // black listed style attributes.
26002 Roo.HtmlEditorCore.cblack= [
26003       //  'font-size' -- this can be set by the project 
26004 ];
26005
26006
26007 Roo.HtmlEditorCore.swapCodes   =[ 
26008     [    8211, "--" ], 
26009     [    8212, "--" ], 
26010     [    8216,  "'" ],  
26011     [    8217, "'" ],  
26012     [    8220, '"' ],  
26013     [    8221, '"' ],  
26014     [    8226, "*" ],  
26015     [    8230, "..." ]
26016 ]; 
26017
26018     //<script type="text/javascript">
26019
26020 /*
26021  * Ext JS Library 1.1.1
26022  * Copyright(c) 2006-2007, Ext JS, LLC.
26023  * Licence LGPL
26024  * 
26025  */
26026  
26027  
26028 Roo.form.HtmlEditor = function(config){
26029     
26030     
26031     
26032     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26033     
26034     if (!this.toolbars) {
26035         this.toolbars = [];
26036     }
26037     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26038     
26039     
26040 };
26041
26042 /**
26043  * @class Roo.form.HtmlEditor
26044  * @extends Roo.form.Field
26045  * Provides a lightweight HTML Editor component.
26046  *
26047  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26048  * 
26049  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26050  * supported by this editor.</b><br/><br/>
26051  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26052  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26053  */
26054 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26055     /**
26056      * @cfg {Boolean} clearUp
26057      */
26058     clearUp : true,
26059       /**
26060      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26061      */
26062     toolbars : false,
26063    
26064      /**
26065      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26066      *                        Roo.resizable.
26067      */
26068     resizable : false,
26069      /**
26070      * @cfg {Number} height (in pixels)
26071      */   
26072     height: 300,
26073    /**
26074      * @cfg {Number} width (in pixels)
26075      */   
26076     width: 500,
26077     
26078     /**
26079      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26080      * 
26081      */
26082     stylesheets: false,
26083     
26084     // id of frame..
26085     frameId: false,
26086     
26087     // private properties
26088     validationEvent : false,
26089     deferHeight: true,
26090     initialized : false,
26091     activated : false,
26092     
26093     onFocus : Roo.emptyFn,
26094     iframePad:3,
26095     hideMode:'offsets',
26096     
26097     defaultAutoCreate : { // modified by initCompnoent..
26098         tag: "textarea",
26099         style:"width:500px;height:300px;",
26100         autocomplete: "off"
26101     },
26102
26103     // private
26104     initComponent : function(){
26105         this.addEvents({
26106             /**
26107              * @event initialize
26108              * Fires when the editor is fully initialized (including the iframe)
26109              * @param {HtmlEditor} this
26110              */
26111             initialize: true,
26112             /**
26113              * @event activate
26114              * Fires when the editor is first receives the focus. Any insertion must wait
26115              * until after this event.
26116              * @param {HtmlEditor} this
26117              */
26118             activate: true,
26119              /**
26120              * @event beforesync
26121              * Fires before the textarea is updated with content from the editor iframe. Return false
26122              * to cancel the sync.
26123              * @param {HtmlEditor} this
26124              * @param {String} html
26125              */
26126             beforesync: true,
26127              /**
26128              * @event beforepush
26129              * Fires before the iframe editor is updated with content from the textarea. Return false
26130              * to cancel the push.
26131              * @param {HtmlEditor} this
26132              * @param {String} html
26133              */
26134             beforepush: true,
26135              /**
26136              * @event sync
26137              * Fires when the textarea is updated with content from the editor iframe.
26138              * @param {HtmlEditor} this
26139              * @param {String} html
26140              */
26141             sync: true,
26142              /**
26143              * @event push
26144              * Fires when the iframe editor is updated with content from the textarea.
26145              * @param {HtmlEditor} this
26146              * @param {String} html
26147              */
26148             push: true,
26149              /**
26150              * @event editmodechange
26151              * Fires when the editor switches edit modes
26152              * @param {HtmlEditor} this
26153              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26154              */
26155             editmodechange: true,
26156             /**
26157              * @event editorevent
26158              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26159              * @param {HtmlEditor} this
26160              */
26161             editorevent: true,
26162             /**
26163              * @event firstfocus
26164              * Fires when on first focus - needed by toolbars..
26165              * @param {HtmlEditor} this
26166              */
26167             firstfocus: true,
26168             /**
26169              * @event autosave
26170              * Auto save the htmlEditor value as a file into Events
26171              * @param {HtmlEditor} this
26172              */
26173             autosave: true,
26174             /**
26175              * @event savedpreview
26176              * preview the saved version of htmlEditor
26177              * @param {HtmlEditor} this
26178              */
26179             savedpreview: true
26180         });
26181         this.defaultAutoCreate =  {
26182             tag: "textarea",
26183             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26184             autocomplete: "off"
26185         };
26186     },
26187
26188     /**
26189      * Protected method that will not generally be called directly. It
26190      * is called when the editor creates its toolbar. Override this method if you need to
26191      * add custom toolbar buttons.
26192      * @param {HtmlEditor} editor
26193      */
26194     createToolbar : function(editor){
26195         Roo.log("create toolbars");
26196         if (!editor.toolbars || !editor.toolbars.length) {
26197             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26198         }
26199         
26200         for (var i =0 ; i < editor.toolbars.length;i++) {
26201             editor.toolbars[i] = Roo.factory(
26202                     typeof(editor.toolbars[i]) == 'string' ?
26203                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26204                 Roo.form.HtmlEditor);
26205             editor.toolbars[i].init(editor);
26206         }
26207          
26208         
26209     },
26210
26211      
26212     // private
26213     onRender : function(ct, position)
26214     {
26215         var _t = this;
26216         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26217         
26218         this.wrap = this.el.wrap({
26219             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26220         });
26221         
26222         this.editorcore.onRender(ct, position);
26223          
26224         if (this.resizable) {
26225             this.resizeEl = new Roo.Resizable(this.wrap, {
26226                 pinned : true,
26227                 wrap: true,
26228                 dynamic : true,
26229                 minHeight : this.height,
26230                 height: this.height,
26231                 handles : this.resizable,
26232                 width: this.width,
26233                 listeners : {
26234                     resize : function(r, w, h) {
26235                         _t.onResize(w,h); // -something
26236                     }
26237                 }
26238             });
26239             
26240         }
26241         this.createToolbar(this);
26242        
26243         
26244         if(!this.width){
26245             this.setSize(this.wrap.getSize());
26246         }
26247         if (this.resizeEl) {
26248             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26249             // should trigger onReize..
26250         }
26251         
26252 //        if(this.autosave && this.w){
26253 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26254 //        }
26255     },
26256
26257     // private
26258     onResize : function(w, h)
26259     {
26260         //Roo.log('resize: ' +w + ',' + h );
26261         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26262         var ew = false;
26263         var eh = false;
26264         
26265         if(this.el ){
26266             if(typeof w == 'number'){
26267                 var aw = w - this.wrap.getFrameWidth('lr');
26268                 this.el.setWidth(this.adjustWidth('textarea', aw));
26269                 ew = aw;
26270             }
26271             if(typeof h == 'number'){
26272                 var tbh = 0;
26273                 for (var i =0; i < this.toolbars.length;i++) {
26274                     // fixme - ask toolbars for heights?
26275                     tbh += this.toolbars[i].tb.el.getHeight();
26276                     if (this.toolbars[i].footer) {
26277                         tbh += this.toolbars[i].footer.el.getHeight();
26278                     }
26279                 }
26280                 
26281                 
26282                 
26283                 
26284                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26285                 ah -= 5; // knock a few pixes off for look..
26286                 this.el.setHeight(this.adjustWidth('textarea', ah));
26287                 var eh = ah;
26288             }
26289         }
26290         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26291         this.editorcore.onResize(ew,eh);
26292         
26293     },
26294
26295     /**
26296      * Toggles the editor between standard and source edit mode.
26297      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26298      */
26299     toggleSourceEdit : function(sourceEditMode)
26300     {
26301         this.editorcore.toggleSourceEdit(sourceEditMode);
26302         
26303         if(this.editorcore.sourceEditMode){
26304             Roo.log('editor - showing textarea');
26305             
26306 //            Roo.log('in');
26307 //            Roo.log(this.syncValue());
26308             this.editorcore.syncValue();
26309             this.el.removeClass('x-hidden');
26310             this.el.dom.removeAttribute('tabIndex');
26311             this.el.focus();
26312         }else{
26313             Roo.log('editor - hiding textarea');
26314 //            Roo.log('out')
26315 //            Roo.log(this.pushValue()); 
26316             this.editorcore.pushValue();
26317             
26318             this.el.addClass('x-hidden');
26319             this.el.dom.setAttribute('tabIndex', -1);
26320             //this.deferFocus();
26321         }
26322          
26323         this.setSize(this.wrap.getSize());
26324         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26325     },
26326  
26327     // private (for BoxComponent)
26328     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26329
26330     // private (for BoxComponent)
26331     getResizeEl : function(){
26332         return this.wrap;
26333     },
26334
26335     // private (for BoxComponent)
26336     getPositionEl : function(){
26337         return this.wrap;
26338     },
26339
26340     // private
26341     initEvents : function(){
26342         this.originalValue = this.getValue();
26343     },
26344
26345     /**
26346      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26347      * @method
26348      */
26349     markInvalid : Roo.emptyFn,
26350     /**
26351      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26352      * @method
26353      */
26354     clearInvalid : Roo.emptyFn,
26355
26356     setValue : function(v){
26357         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26358         this.editorcore.pushValue();
26359     },
26360
26361      
26362     // private
26363     deferFocus : function(){
26364         this.focus.defer(10, this);
26365     },
26366
26367     // doc'ed in Field
26368     focus : function(){
26369         this.editorcore.focus();
26370         
26371     },
26372       
26373
26374     // private
26375     onDestroy : function(){
26376         
26377         
26378         
26379         if(this.rendered){
26380             
26381             for (var i =0; i < this.toolbars.length;i++) {
26382                 // fixme - ask toolbars for heights?
26383                 this.toolbars[i].onDestroy();
26384             }
26385             
26386             this.wrap.dom.innerHTML = '';
26387             this.wrap.remove();
26388         }
26389     },
26390
26391     // private
26392     onFirstFocus : function(){
26393         //Roo.log("onFirstFocus");
26394         this.editorcore.onFirstFocus();
26395          for (var i =0; i < this.toolbars.length;i++) {
26396             this.toolbars[i].onFirstFocus();
26397         }
26398         
26399     },
26400     
26401     // private
26402     syncValue : function()
26403     {
26404         this.editorcore.syncValue();
26405     },
26406     
26407     pushValue : function()
26408     {
26409         this.editorcore.pushValue();
26410     }
26411      
26412     
26413     // hide stuff that is not compatible
26414     /**
26415      * @event blur
26416      * @hide
26417      */
26418     /**
26419      * @event change
26420      * @hide
26421      */
26422     /**
26423      * @event focus
26424      * @hide
26425      */
26426     /**
26427      * @event specialkey
26428      * @hide
26429      */
26430     /**
26431      * @cfg {String} fieldClass @hide
26432      */
26433     /**
26434      * @cfg {String} focusClass @hide
26435      */
26436     /**
26437      * @cfg {String} autoCreate @hide
26438      */
26439     /**
26440      * @cfg {String} inputType @hide
26441      */
26442     /**
26443      * @cfg {String} invalidClass @hide
26444      */
26445     /**
26446      * @cfg {String} invalidText @hide
26447      */
26448     /**
26449      * @cfg {String} msgFx @hide
26450      */
26451     /**
26452      * @cfg {String} validateOnBlur @hide
26453      */
26454 });
26455  
26456     // <script type="text/javascript">
26457 /*
26458  * Based on
26459  * Ext JS Library 1.1.1
26460  * Copyright(c) 2006-2007, Ext JS, LLC.
26461  *  
26462  
26463  */
26464
26465 /**
26466  * @class Roo.form.HtmlEditorToolbar1
26467  * Basic Toolbar
26468  * 
26469  * Usage:
26470  *
26471  new Roo.form.HtmlEditor({
26472     ....
26473     toolbars : [
26474         new Roo.form.HtmlEditorToolbar1({
26475             disable : { fonts: 1 , format: 1, ..., ... , ...],
26476             btns : [ .... ]
26477         })
26478     }
26479      
26480  * 
26481  * @cfg {Object} disable List of elements to disable..
26482  * @cfg {Array} btns List of additional buttons.
26483  * 
26484  * 
26485  * NEEDS Extra CSS? 
26486  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26487  */
26488  
26489 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26490 {
26491     
26492     Roo.apply(this, config);
26493     
26494     // default disabled, based on 'good practice'..
26495     this.disable = this.disable || {};
26496     Roo.applyIf(this.disable, {
26497         fontSize : true,
26498         colors : true,
26499         specialElements : true
26500     });
26501     
26502     
26503     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26504     // dont call parent... till later.
26505 }
26506
26507 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26508     
26509     tb: false,
26510     
26511     rendered: false,
26512     
26513     editor : false,
26514     editorcore : false,
26515     /**
26516      * @cfg {Object} disable  List of toolbar elements to disable
26517          
26518      */
26519     disable : false,
26520     
26521     
26522      /**
26523      * @cfg {String} createLinkText The default text for the create link prompt
26524      */
26525     createLinkText : 'Please enter the URL for the link:',
26526     /**
26527      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
26528      */
26529     defaultLinkValue : 'http:/'+'/',
26530    
26531     
26532       /**
26533      * @cfg {Array} fontFamilies An array of available font families
26534      */
26535     fontFamilies : [
26536         'Arial',
26537         'Courier New',
26538         'Tahoma',
26539         'Times New Roman',
26540         'Verdana'
26541     ],
26542     
26543     specialChars : [
26544            "&#169;",
26545           "&#174;",     
26546           "&#8482;",    
26547           "&#163;" ,    
26548          // "&#8212;",    
26549           "&#8230;",    
26550           "&#247;" ,    
26551         //  "&#225;" ,     ?? a acute?
26552            "&#8364;"    , //Euro
26553        //   "&#8220;"    ,
26554         //  "&#8221;"    ,
26555         //  "&#8226;"    ,
26556           "&#176;"  //   , // degrees
26557
26558          // "&#233;"     , // e ecute
26559          // "&#250;"     , // u ecute?
26560     ],
26561     
26562     specialElements : [
26563         {
26564             text: "Insert Table",
26565             xtype: 'MenuItem',
26566             xns : Roo.Menu,
26567             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26568                 
26569         },
26570         {    
26571             text: "Insert Image",
26572             xtype: 'MenuItem',
26573             xns : Roo.Menu,
26574             ihtml : '<img src="about:blank"/>'
26575             
26576         }
26577         
26578          
26579     ],
26580     
26581     
26582     inputElements : [ 
26583             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26584             "input:submit", "input:button", "select", "textarea", "label" ],
26585     formats : [
26586         ["p"] ,  
26587         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26588         ["pre"],[ "code"], 
26589         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26590         ['div'],['span']
26591     ],
26592     
26593     cleanStyles : [
26594         "font-size"
26595     ],
26596      /**
26597      * @cfg {String} defaultFont default font to use.
26598      */
26599     defaultFont: 'tahoma',
26600    
26601     fontSelect : false,
26602     
26603     
26604     formatCombo : false,
26605     
26606     init : function(editor)
26607     {
26608         this.editor = editor;
26609         this.editorcore = editor.editorcore ? editor.editorcore : editor;
26610         var editorcore = this.editorcore;
26611         
26612         var _t = this;
26613         
26614         var fid = editorcore.frameId;
26615         var etb = this;
26616         function btn(id, toggle, handler){
26617             var xid = fid + '-'+ id ;
26618             return {
26619                 id : xid,
26620                 cmd : id,
26621                 cls : 'x-btn-icon x-edit-'+id,
26622                 enableToggle:toggle !== false,
26623                 scope: _t, // was editor...
26624                 handler:handler||_t.relayBtnCmd,
26625                 clickEvent:'mousedown',
26626                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26627                 tabIndex:-1
26628             };
26629         }
26630         
26631         
26632         
26633         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26634         this.tb = tb;
26635          // stop form submits
26636         tb.el.on('click', function(e){
26637             e.preventDefault(); // what does this do?
26638         });
26639
26640         if(!this.disable.font) { // && !Roo.isSafari){
26641             /* why no safari for fonts 
26642             editor.fontSelect = tb.el.createChild({
26643                 tag:'select',
26644                 tabIndex: -1,
26645                 cls:'x-font-select',
26646                 html: this.createFontOptions()
26647             });
26648             
26649             editor.fontSelect.on('change', function(){
26650                 var font = editor.fontSelect.dom.value;
26651                 editor.relayCmd('fontname', font);
26652                 editor.deferFocus();
26653             }, editor);
26654             
26655             tb.add(
26656                 editor.fontSelect.dom,
26657                 '-'
26658             );
26659             */
26660             
26661         };
26662         if(!this.disable.formats){
26663             this.formatCombo = new Roo.form.ComboBox({
26664                 store: new Roo.data.SimpleStore({
26665                     id : 'tag',
26666                     fields: ['tag'],
26667                     data : this.formats // from states.js
26668                 }),
26669                 blockFocus : true,
26670                 name : '',
26671                 //autoCreate : {tag: "div",  size: "20"},
26672                 displayField:'tag',
26673                 typeAhead: false,
26674                 mode: 'local',
26675                 editable : false,
26676                 triggerAction: 'all',
26677                 emptyText:'Add tag',
26678                 selectOnFocus:true,
26679                 width:135,
26680                 listeners : {
26681                     'select': function(c, r, i) {
26682                         editorcore.insertTag(r.get('tag'));
26683                         editor.focus();
26684                     }
26685                 }
26686
26687             });
26688             tb.addField(this.formatCombo);
26689             
26690         }
26691         
26692         if(!this.disable.format){
26693             tb.add(
26694                 btn('bold'),
26695                 btn('italic'),
26696                 btn('underline')
26697             );
26698         };
26699         if(!this.disable.fontSize){
26700             tb.add(
26701                 '-',
26702                 
26703                 
26704                 btn('increasefontsize', false, editorcore.adjustFont),
26705                 btn('decreasefontsize', false, editorcore.adjustFont)
26706             );
26707         };
26708         
26709         
26710         if(!this.disable.colors){
26711             tb.add(
26712                 '-', {
26713                     id:editorcore.frameId +'-forecolor',
26714                     cls:'x-btn-icon x-edit-forecolor',
26715                     clickEvent:'mousedown',
26716                     tooltip: this.buttonTips['forecolor'] || undefined,
26717                     tabIndex:-1,
26718                     menu : new Roo.menu.ColorMenu({
26719                         allowReselect: true,
26720                         focus: Roo.emptyFn,
26721                         value:'000000',
26722                         plain:true,
26723                         selectHandler: function(cp, color){
26724                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26725                             editor.deferFocus();
26726                         },
26727                         scope: editorcore,
26728                         clickEvent:'mousedown'
26729                     })
26730                 }, {
26731                     id:editorcore.frameId +'backcolor',
26732                     cls:'x-btn-icon x-edit-backcolor',
26733                     clickEvent:'mousedown',
26734                     tooltip: this.buttonTips['backcolor'] || undefined,
26735                     tabIndex:-1,
26736                     menu : new Roo.menu.ColorMenu({
26737                         focus: Roo.emptyFn,
26738                         value:'FFFFFF',
26739                         plain:true,
26740                         allowReselect: true,
26741                         selectHandler: function(cp, color){
26742                             if(Roo.isGecko){
26743                                 editorcore.execCmd('useCSS', false);
26744                                 editorcore.execCmd('hilitecolor', color);
26745                                 editorcore.execCmd('useCSS', true);
26746                                 editor.deferFocus();
26747                             }else{
26748                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26749                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26750                                 editor.deferFocus();
26751                             }
26752                         },
26753                         scope:editorcore,
26754                         clickEvent:'mousedown'
26755                     })
26756                 }
26757             );
26758         };
26759         // now add all the items...
26760         
26761
26762         if(!this.disable.alignments){
26763             tb.add(
26764                 '-',
26765                 btn('justifyleft'),
26766                 btn('justifycenter'),
26767                 btn('justifyright')
26768             );
26769         };
26770
26771         //if(!Roo.isSafari){
26772             if(!this.disable.links){
26773                 tb.add(
26774                     '-',
26775                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
26776                 );
26777             };
26778
26779             if(!this.disable.lists){
26780                 tb.add(
26781                     '-',
26782                     btn('insertorderedlist'),
26783                     btn('insertunorderedlist')
26784                 );
26785             }
26786             if(!this.disable.sourceEdit){
26787                 tb.add(
26788                     '-',
26789                     btn('sourceedit', true, function(btn){
26790                         Roo.log(this);
26791                         this.toggleSourceEdit(btn.pressed);
26792                     })
26793                 );
26794             }
26795         //}
26796         
26797         var smenu = { };
26798         // special menu.. - needs to be tidied up..
26799         if (!this.disable.special) {
26800             smenu = {
26801                 text: "&#169;",
26802                 cls: 'x-edit-none',
26803                 
26804                 menu : {
26805                     items : []
26806                 }
26807             };
26808             for (var i =0; i < this.specialChars.length; i++) {
26809                 smenu.menu.items.push({
26810                     
26811                     html: this.specialChars[i],
26812                     handler: function(a,b) {
26813                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26814                         //editor.insertAtCursor(a.html);
26815                         
26816                     },
26817                     tabIndex:-1
26818                 });
26819             }
26820             
26821             
26822             tb.add(smenu);
26823             
26824             
26825         }
26826         
26827         var cmenu = { };
26828         if (!this.disable.cleanStyles) {
26829             cmenu = {
26830                 cls: 'x-btn-icon x-btn-clear',
26831                 
26832                 menu : {
26833                     items : []
26834                 }
26835             };
26836             for (var i =0; i < this.cleanStyles.length; i++) {
26837                 cmenu.menu.items.push({
26838                     actiontype : this.cleanStyles[i],
26839                     html: 'Remove ' + this.cleanStyles[i],
26840                     handler: function(a,b) {
26841                         Roo.log(a);
26842                         Roo.log(b);
26843                         var c = Roo.get(editorcore.doc.body);
26844                         c.select('[style]').each(function(s) {
26845                             s.dom.style.removeProperty(a.actiontype);
26846                         });
26847                         editorcore.syncValue();
26848                     },
26849                     tabIndex:-1
26850                 });
26851             }
26852             cmenu.menu.items.push({
26853                 actiontype : 'word',
26854                 html: 'Remove MS Word Formating',
26855                 handler: function(a,b) {
26856                     editorcore.cleanWord();
26857                     editorcore.syncValue();
26858                 },
26859                 tabIndex:-1
26860             });
26861             
26862             cmenu.menu.items.push({
26863                 actiontype : 'all',
26864                 html: 'Remove All Styles',
26865                 handler: function(a,b) {
26866                     
26867                     var c = Roo.get(editorcore.doc.body);
26868                     c.select('[style]').each(function(s) {
26869                         s.dom.removeAttribute('style');
26870                     });
26871                     editorcore.syncValue();
26872                 },
26873                 tabIndex:-1
26874             });
26875              cmenu.menu.items.push({
26876                 actiontype : 'word',
26877                 html: 'Tidy HTML Source',
26878                 handler: function(a,b) {
26879                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
26880                     editorcore.syncValue();
26881                 },
26882                 tabIndex:-1
26883             });
26884             
26885             
26886             tb.add(cmenu);
26887         }
26888          
26889         if (!this.disable.specialElements) {
26890             var semenu = {
26891                 text: "Other;",
26892                 cls: 'x-edit-none',
26893                 menu : {
26894                     items : []
26895                 }
26896             };
26897             for (var i =0; i < this.specialElements.length; i++) {
26898                 semenu.menu.items.push(
26899                     Roo.apply({ 
26900                         handler: function(a,b) {
26901                             editor.insertAtCursor(this.ihtml);
26902                         }
26903                     }, this.specialElements[i])
26904                 );
26905                     
26906             }
26907             
26908             tb.add(semenu);
26909             
26910             
26911         }
26912          
26913         
26914         if (this.btns) {
26915             for(var i =0; i< this.btns.length;i++) {
26916                 var b = Roo.factory(this.btns[i],Roo.form);
26917                 b.cls =  'x-edit-none';
26918                 b.scope = editorcore;
26919                 tb.add(b);
26920             }
26921         
26922         }
26923         
26924         
26925         
26926         // disable everything...
26927         
26928         this.tb.items.each(function(item){
26929            if(item.id != editorcore.frameId+ '-sourceedit'){
26930                 item.disable();
26931             }
26932         });
26933         this.rendered = true;
26934         
26935         // the all the btns;
26936         editor.on('editorevent', this.updateToolbar, this);
26937         // other toolbars need to implement this..
26938         //editor.on('editmodechange', this.updateToolbar, this);
26939     },
26940     
26941     
26942     relayBtnCmd : function(btn) {
26943         this.editorcore.relayCmd(btn.cmd);
26944     },
26945     // private used internally
26946     createLink : function(){
26947         Roo.log("create link?");
26948         var url = prompt(this.createLinkText, this.defaultLinkValue);
26949         if(url && url != 'http:/'+'/'){
26950             this.editorcore.relayCmd('createlink', url);
26951         }
26952     },
26953
26954     
26955     /**
26956      * Protected method that will not generally be called directly. It triggers
26957      * a toolbar update by reading the markup state of the current selection in the editor.
26958      */
26959     updateToolbar: function(){
26960
26961         if(!this.editorcore.activated){
26962             this.editor.onFirstFocus();
26963             return;
26964         }
26965
26966         var btns = this.tb.items.map, 
26967             doc = this.editorcore.doc,
26968             frameId = this.editorcore.frameId;
26969
26970         if(!this.disable.font && !Roo.isSafari){
26971             /*
26972             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26973             if(name != this.fontSelect.dom.value){
26974                 this.fontSelect.dom.value = name;
26975             }
26976             */
26977         }
26978         if(!this.disable.format){
26979             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26980             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26981             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26982         }
26983         if(!this.disable.alignments){
26984             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26985             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26986             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26987         }
26988         if(!Roo.isSafari && !this.disable.lists){
26989             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26990             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26991         }
26992         
26993         var ans = this.editorcore.getAllAncestors();
26994         if (this.formatCombo) {
26995             
26996             
26997             var store = this.formatCombo.store;
26998             this.formatCombo.setValue("");
26999             for (var i =0; i < ans.length;i++) {
27000                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27001                     // select it..
27002                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27003                     break;
27004                 }
27005             }
27006         }
27007         
27008         
27009         
27010         // hides menus... - so this cant be on a menu...
27011         Roo.menu.MenuMgr.hideAll();
27012
27013         //this.editorsyncValue();
27014     },
27015    
27016     
27017     createFontOptions : function(){
27018         var buf = [], fs = this.fontFamilies, ff, lc;
27019         
27020         
27021         
27022         for(var i = 0, len = fs.length; i< len; i++){
27023             ff = fs[i];
27024             lc = ff.toLowerCase();
27025             buf.push(
27026                 '<option value="',lc,'" style="font-family:',ff,';"',
27027                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27028                     ff,
27029                 '</option>'
27030             );
27031         }
27032         return buf.join('');
27033     },
27034     
27035     toggleSourceEdit : function(sourceEditMode){
27036         
27037         Roo.log("toolbar toogle");
27038         if(sourceEditMode === undefined){
27039             sourceEditMode = !this.sourceEditMode;
27040         }
27041         this.sourceEditMode = sourceEditMode === true;
27042         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27043         // just toggle the button?
27044         if(btn.pressed !== this.sourceEditMode){
27045             btn.toggle(this.sourceEditMode);
27046             return;
27047         }
27048         
27049         if(sourceEditMode){
27050             Roo.log("disabling buttons");
27051             this.tb.items.each(function(item){
27052                 if(item.cmd != 'sourceedit'){
27053                     item.disable();
27054                 }
27055             });
27056           
27057         }else{
27058             Roo.log("enabling buttons");
27059             if(this.editorcore.initialized){
27060                 this.tb.items.each(function(item){
27061                     item.enable();
27062                 });
27063             }
27064             
27065         }
27066         Roo.log("calling toggole on editor");
27067         // tell the editor that it's been pressed..
27068         this.editor.toggleSourceEdit(sourceEditMode);
27069        
27070     },
27071      /**
27072      * Object collection of toolbar tooltips for the buttons in the editor. The key
27073      * is the command id associated with that button and the value is a valid QuickTips object.
27074      * For example:
27075 <pre><code>
27076 {
27077     bold : {
27078         title: 'Bold (Ctrl+B)',
27079         text: 'Make the selected text bold.',
27080         cls: 'x-html-editor-tip'
27081     },
27082     italic : {
27083         title: 'Italic (Ctrl+I)',
27084         text: 'Make the selected text italic.',
27085         cls: 'x-html-editor-tip'
27086     },
27087     ...
27088 </code></pre>
27089     * @type Object
27090      */
27091     buttonTips : {
27092         bold : {
27093             title: 'Bold (Ctrl+B)',
27094             text: 'Make the selected text bold.',
27095             cls: 'x-html-editor-tip'
27096         },
27097         italic : {
27098             title: 'Italic (Ctrl+I)',
27099             text: 'Make the selected text italic.',
27100             cls: 'x-html-editor-tip'
27101         },
27102         underline : {
27103             title: 'Underline (Ctrl+U)',
27104             text: 'Underline the selected text.',
27105             cls: 'x-html-editor-tip'
27106         },
27107         increasefontsize : {
27108             title: 'Grow Text',
27109             text: 'Increase the font size.',
27110             cls: 'x-html-editor-tip'
27111         },
27112         decreasefontsize : {
27113             title: 'Shrink Text',
27114             text: 'Decrease the font size.',
27115             cls: 'x-html-editor-tip'
27116         },
27117         backcolor : {
27118             title: 'Text Highlight Color',
27119             text: 'Change the background color of the selected text.',
27120             cls: 'x-html-editor-tip'
27121         },
27122         forecolor : {
27123             title: 'Font Color',
27124             text: 'Change the color of the selected text.',
27125             cls: 'x-html-editor-tip'
27126         },
27127         justifyleft : {
27128             title: 'Align Text Left',
27129             text: 'Align text to the left.',
27130             cls: 'x-html-editor-tip'
27131         },
27132         justifycenter : {
27133             title: 'Center Text',
27134             text: 'Center text in the editor.',
27135             cls: 'x-html-editor-tip'
27136         },
27137         justifyright : {
27138             title: 'Align Text Right',
27139             text: 'Align text to the right.',
27140             cls: 'x-html-editor-tip'
27141         },
27142         insertunorderedlist : {
27143             title: 'Bullet List',
27144             text: 'Start a bulleted list.',
27145             cls: 'x-html-editor-tip'
27146         },
27147         insertorderedlist : {
27148             title: 'Numbered List',
27149             text: 'Start a numbered list.',
27150             cls: 'x-html-editor-tip'
27151         },
27152         createlink : {
27153             title: 'Hyperlink',
27154             text: 'Make the selected text a hyperlink.',
27155             cls: 'x-html-editor-tip'
27156         },
27157         sourceedit : {
27158             title: 'Source Edit',
27159             text: 'Switch to source editing mode.',
27160             cls: 'x-html-editor-tip'
27161         }
27162     },
27163     // private
27164     onDestroy : function(){
27165         if(this.rendered){
27166             
27167             this.tb.items.each(function(item){
27168                 if(item.menu){
27169                     item.menu.removeAll();
27170                     if(item.menu.el){
27171                         item.menu.el.destroy();
27172                     }
27173                 }
27174                 item.destroy();
27175             });
27176              
27177         }
27178     },
27179     onFirstFocus: function() {
27180         this.tb.items.each(function(item){
27181            item.enable();
27182         });
27183     }
27184 });
27185
27186
27187
27188
27189 // <script type="text/javascript">
27190 /*
27191  * Based on
27192  * Ext JS Library 1.1.1
27193  * Copyright(c) 2006-2007, Ext JS, LLC.
27194  *  
27195  
27196  */
27197
27198  
27199 /**
27200  * @class Roo.form.HtmlEditor.ToolbarContext
27201  * Context Toolbar
27202  * 
27203  * Usage:
27204  *
27205  new Roo.form.HtmlEditor({
27206     ....
27207     toolbars : [
27208         { xtype: 'ToolbarStandard', styles : {} }
27209         { xtype: 'ToolbarContext', disable : {} }
27210     ]
27211 })
27212
27213      
27214  * 
27215  * @config : {Object} disable List of elements to disable.. (not done yet.)
27216  * @config : {Object} styles  Map of styles available.
27217  * 
27218  */
27219
27220 Roo.form.HtmlEditor.ToolbarContext = function(config)
27221 {
27222     
27223     Roo.apply(this, config);
27224     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27225     // dont call parent... till later.
27226     this.styles = this.styles || {};
27227 }
27228
27229  
27230
27231 Roo.form.HtmlEditor.ToolbarContext.types = {
27232     'IMG' : {
27233         width : {
27234             title: "Width",
27235             width: 40
27236         },
27237         height:  {
27238             title: "Height",
27239             width: 40
27240         },
27241         align: {
27242             title: "Align",
27243             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27244             width : 80
27245             
27246         },
27247         border: {
27248             title: "Border",
27249             width: 40
27250         },
27251         alt: {
27252             title: "Alt",
27253             width: 120
27254         },
27255         src : {
27256             title: "Src",
27257             width: 220
27258         }
27259         
27260     },
27261     'A' : {
27262         name : {
27263             title: "Name",
27264             width: 50
27265         },
27266         target:  {
27267             title: "Target",
27268             width: 120
27269         },
27270         href:  {
27271             title: "Href",
27272             width: 220
27273         } // border?
27274         
27275     },
27276     'TABLE' : {
27277         rows : {
27278             title: "Rows",
27279             width: 20
27280         },
27281         cols : {
27282             title: "Cols",
27283             width: 20
27284         },
27285         width : {
27286             title: "Width",
27287             width: 40
27288         },
27289         height : {
27290             title: "Height",
27291             width: 40
27292         },
27293         border : {
27294             title: "Border",
27295             width: 20
27296         }
27297     },
27298     'TD' : {
27299         width : {
27300             title: "Width",
27301             width: 40
27302         },
27303         height : {
27304             title: "Height",
27305             width: 40
27306         },   
27307         align: {
27308             title: "Align",
27309             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27310             width: 80
27311         },
27312         valign: {
27313             title: "Valign",
27314             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27315             width: 80
27316         },
27317         colspan: {
27318             title: "Colspan",
27319             width: 20
27320             
27321         },
27322          'font-family'  : {
27323             title : "Font",
27324             style : 'fontFamily',
27325             displayField: 'display',
27326             optname : 'font-family',
27327             width: 140
27328         }
27329     },
27330     'INPUT' : {
27331         name : {
27332             title: "name",
27333             width: 120
27334         },
27335         value : {
27336             title: "Value",
27337             width: 120
27338         },
27339         width : {
27340             title: "Width",
27341             width: 40
27342         }
27343     },
27344     'LABEL' : {
27345         'for' : {
27346             title: "For",
27347             width: 120
27348         }
27349     },
27350     'TEXTAREA' : {
27351           name : {
27352             title: "name",
27353             width: 120
27354         },
27355         rows : {
27356             title: "Rows",
27357             width: 20
27358         },
27359         cols : {
27360             title: "Cols",
27361             width: 20
27362         }
27363     },
27364     'SELECT' : {
27365         name : {
27366             title: "name",
27367             width: 120
27368         },
27369         selectoptions : {
27370             title: "Options",
27371             width: 200
27372         }
27373     },
27374     
27375     // should we really allow this??
27376     // should this just be 
27377     'BODY' : {
27378         title : {
27379             title: "Title",
27380             width: 200,
27381             disabled : true
27382         }
27383     },
27384     'SPAN' : {
27385         'font-family'  : {
27386             title : "Font",
27387             style : 'fontFamily',
27388             displayField: 'display',
27389             optname : 'font-family',
27390             width: 140
27391         }
27392     },
27393     'DIV' : {
27394         'font-family'  : {
27395             title : "Font",
27396             style : 'fontFamily',
27397             displayField: 'display',
27398             optname : 'font-family',
27399             width: 140
27400         }
27401     },
27402      'P' : {
27403         'font-family'  : {
27404             title : "Font",
27405             style : 'fontFamily',
27406             displayField: 'display',
27407             optname : 'font-family',
27408             width: 140
27409         }
27410     },
27411     
27412     '*' : {
27413         // empty..
27414     }
27415
27416 };
27417
27418 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27419 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27420
27421 Roo.form.HtmlEditor.ToolbarContext.options = {
27422         'font-family'  : [ 
27423                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27424                 [ 'Courier New', 'Courier New'],
27425                 [ 'Tahoma', 'Tahoma'],
27426                 [ 'Times New Roman,serif', 'Times'],
27427                 [ 'Verdana','Verdana' ]
27428         ]
27429 };
27430
27431 // fixme - these need to be configurable..
27432  
27433
27434 Roo.form.HtmlEditor.ToolbarContext.types
27435
27436
27437 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27438     
27439     tb: false,
27440     
27441     rendered: false,
27442     
27443     editor : false,
27444     editorcore : false,
27445     /**
27446      * @cfg {Object} disable  List of toolbar elements to disable
27447          
27448      */
27449     disable : false,
27450     /**
27451      * @cfg {Object} styles List of styles 
27452      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27453      *
27454      * These must be defined in the page, so they get rendered correctly..
27455      * .headline { }
27456      * TD.underline { }
27457      * 
27458      */
27459     styles : false,
27460     
27461     options: false,
27462     
27463     toolbars : false,
27464     
27465     init : function(editor)
27466     {
27467         this.editor = editor;
27468         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27469         var editorcore = this.editorcore;
27470         
27471         var fid = editorcore.frameId;
27472         var etb = this;
27473         function btn(id, toggle, handler){
27474             var xid = fid + '-'+ id ;
27475             return {
27476                 id : xid,
27477                 cmd : id,
27478                 cls : 'x-btn-icon x-edit-'+id,
27479                 enableToggle:toggle !== false,
27480                 scope: editorcore, // was editor...
27481                 handler:handler||editorcore.relayBtnCmd,
27482                 clickEvent:'mousedown',
27483                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27484                 tabIndex:-1
27485             };
27486         }
27487         // create a new element.
27488         var wdiv = editor.wrap.createChild({
27489                 tag: 'div'
27490             }, editor.wrap.dom.firstChild.nextSibling, true);
27491         
27492         // can we do this more than once??
27493         
27494          // stop form submits
27495       
27496  
27497         // disable everything...
27498         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27499         this.toolbars = {};
27500            
27501         for (var i in  ty) {
27502           
27503             this.toolbars[i] = this.buildToolbar(ty[i],i);
27504         }
27505         this.tb = this.toolbars.BODY;
27506         this.tb.el.show();
27507         this.buildFooter();
27508         this.footer.show();
27509         editor.on('hide', function( ) { this.footer.hide() }, this);
27510         editor.on('show', function( ) { this.footer.show() }, this);
27511         
27512          
27513         this.rendered = true;
27514         
27515         // the all the btns;
27516         editor.on('editorevent', this.updateToolbar, this);
27517         // other toolbars need to implement this..
27518         //editor.on('editmodechange', this.updateToolbar, this);
27519     },
27520     
27521     
27522     
27523     /**
27524      * Protected method that will not generally be called directly. It triggers
27525      * a toolbar update by reading the markup state of the current selection in the editor.
27526      */
27527     updateToolbar: function(editor,ev,sel){
27528
27529         //Roo.log(ev);
27530         // capture mouse up - this is handy for selecting images..
27531         // perhaps should go somewhere else...
27532         if(!this.editorcore.activated){
27533              this.editor.onFirstFocus();
27534             return;
27535         }
27536         
27537         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27538         // selectNode - might want to handle IE?
27539         if (ev &&
27540             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27541             ev.target && ev.target.tagName == 'IMG') {
27542             // they have click on an image...
27543             // let's see if we can change the selection...
27544             sel = ev.target;
27545          
27546               var nodeRange = sel.ownerDocument.createRange();
27547             try {
27548                 nodeRange.selectNode(sel);
27549             } catch (e) {
27550                 nodeRange.selectNodeContents(sel);
27551             }
27552             //nodeRange.collapse(true);
27553             var s = this.editorcore.win.getSelection();
27554             s.removeAllRanges();
27555             s.addRange(nodeRange);
27556         }  
27557         
27558       
27559         var updateFooter = sel ? false : true;
27560         
27561         
27562         var ans = this.editorcore.getAllAncestors();
27563         
27564         // pick
27565         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27566         
27567         if (!sel) { 
27568             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
27569             sel = sel ? sel : this.editorcore.doc.body;
27570             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
27571             
27572         }
27573         // pick a menu that exists..
27574         var tn = sel.tagName.toUpperCase();
27575         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27576         
27577         tn = sel.tagName.toUpperCase();
27578         
27579         var lastSel = this.tb.selectedNode
27580         
27581         this.tb.selectedNode = sel;
27582         
27583         // if current menu does not match..
27584         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27585                 
27586             this.tb.el.hide();
27587             ///console.log("show: " + tn);
27588             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27589             this.tb.el.show();
27590             // update name
27591             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27592             
27593             
27594             // update attributes
27595             if (this.tb.fields) {
27596                 this.tb.fields.each(function(e) {
27597                     if (e.stylename) {
27598                         e.setValue(sel.style[e.stylename]);
27599                         return;
27600                     } 
27601                    e.setValue(sel.getAttribute(e.attrname));
27602                 });
27603             }
27604             
27605             var hasStyles = false;
27606             for(var i in this.styles) {
27607                 hasStyles = true;
27608                 break;
27609             }
27610             
27611             // update styles
27612             if (hasStyles) { 
27613                 var st = this.tb.fields.item(0);
27614                 
27615                 st.store.removeAll();
27616                
27617                 
27618                 var cn = sel.className.split(/\s+/);
27619                 
27620                 var avs = [];
27621                 if (this.styles['*']) {
27622                     
27623                     Roo.each(this.styles['*'], function(v) {
27624                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27625                     });
27626                 }
27627                 if (this.styles[tn]) { 
27628                     Roo.each(this.styles[tn], function(v) {
27629                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27630                     });
27631                 }
27632                 
27633                 st.store.loadData(avs);
27634                 st.collapse();
27635                 st.setValue(cn);
27636             }
27637             // flag our selected Node.
27638             this.tb.selectedNode = sel;
27639            
27640            
27641             Roo.menu.MenuMgr.hideAll();
27642
27643         }
27644         
27645         if (!updateFooter) {
27646             //this.footDisp.dom.innerHTML = ''; 
27647             return;
27648         }
27649         // update the footer
27650         //
27651         var html = '';
27652         
27653         this.footerEls = ans.reverse();
27654         Roo.each(this.footerEls, function(a,i) {
27655             if (!a) { return; }
27656             html += html.length ? ' &gt; '  :  '';
27657             
27658             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27659             
27660         });
27661        
27662         // 
27663         var sz = this.footDisp.up('td').getSize();
27664         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27665         this.footDisp.dom.style.marginLeft = '5px';
27666         
27667         this.footDisp.dom.style.overflow = 'hidden';
27668         
27669         this.footDisp.dom.innerHTML = html;
27670             
27671         //this.editorsyncValue();
27672     },
27673      
27674     
27675    
27676        
27677     // private
27678     onDestroy : function(){
27679         if(this.rendered){
27680             
27681             this.tb.items.each(function(item){
27682                 if(item.menu){
27683                     item.menu.removeAll();
27684                     if(item.menu.el){
27685                         item.menu.el.destroy();
27686                     }
27687                 }
27688                 item.destroy();
27689             });
27690              
27691         }
27692     },
27693     onFirstFocus: function() {
27694         // need to do this for all the toolbars..
27695         this.tb.items.each(function(item){
27696            item.enable();
27697         });
27698     },
27699     buildToolbar: function(tlist, nm)
27700     {
27701         var editor = this.editor;
27702         var editorcore = this.editorcore;
27703          // create a new element.
27704         var wdiv = editor.wrap.createChild({
27705                 tag: 'div'
27706             }, editor.wrap.dom.firstChild.nextSibling, true);
27707         
27708        
27709         var tb = new Roo.Toolbar(wdiv);
27710         // add the name..
27711         
27712         tb.add(nm+ ":&nbsp;");
27713         
27714         var styles = [];
27715         for(var i in this.styles) {
27716             styles.push(i);
27717         }
27718         
27719         // styles...
27720         if (styles && styles.length) {
27721             
27722             // this needs a multi-select checkbox...
27723             tb.addField( new Roo.form.ComboBox({
27724                 store: new Roo.data.SimpleStore({
27725                     id : 'val',
27726                     fields: ['val', 'selected'],
27727                     data : [] 
27728                 }),
27729                 name : '-roo-edit-className',
27730                 attrname : 'className',
27731                 displayField: 'val',
27732                 typeAhead: false,
27733                 mode: 'local',
27734                 editable : false,
27735                 triggerAction: 'all',
27736                 emptyText:'Select Style',
27737                 selectOnFocus:true,
27738                 width: 130,
27739                 listeners : {
27740                     'select': function(c, r, i) {
27741                         // initial support only for on class per el..
27742                         tb.selectedNode.className =  r ? r.get('val') : '';
27743                         editorcore.syncValue();
27744                     }
27745                 }
27746     
27747             }));
27748         }
27749         
27750         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27751         var tbops = tbc.options;
27752         
27753         for (var i in tlist) {
27754             
27755             var item = tlist[i];
27756             tb.add(item.title + ":&nbsp;");
27757             
27758             
27759             //optname == used so you can configure the options available..
27760             var opts = item.opts ? item.opts : false;
27761             if (item.optname) {
27762                 opts = tbops[item.optname];
27763            
27764             }
27765             
27766             if (opts) {
27767                 // opts == pulldown..
27768                 tb.addField( new Roo.form.ComboBox({
27769                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27770                         id : 'val',
27771                         fields: ['val', 'display'],
27772                         data : opts  
27773                     }),
27774                     name : '-roo-edit-' + i,
27775                     attrname : i,
27776                     stylename : item.style ? item.style : false,
27777                     displayField: item.displayField ? item.displayField : 'val',
27778                     valueField :  'val',
27779                     typeAhead: false,
27780                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27781                     editable : false,
27782                     triggerAction: 'all',
27783                     emptyText:'Select',
27784                     selectOnFocus:true,
27785                     width: item.width ? item.width  : 130,
27786                     listeners : {
27787                         'select': function(c, r, i) {
27788                             if (c.stylename) {
27789                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27790                                 return;
27791                             }
27792                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27793                         }
27794                     }
27795
27796                 }));
27797                 continue;
27798                     
27799                  
27800                 
27801                 tb.addField( new Roo.form.TextField({
27802                     name: i,
27803                     width: 100,
27804                     //allowBlank:false,
27805                     value: ''
27806                 }));
27807                 continue;
27808             }
27809             tb.addField( new Roo.form.TextField({
27810                 name: '-roo-edit-' + i,
27811                 attrname : i,
27812                 
27813                 width: item.width,
27814                 //allowBlank:true,
27815                 value: '',
27816                 listeners: {
27817                     'change' : function(f, nv, ov) {
27818                         tb.selectedNode.setAttribute(f.attrname, nv);
27819                     }
27820                 }
27821             }));
27822              
27823         }
27824         tb.addFill();
27825         var _this = this;
27826         tb.addButton( {
27827             text: 'Remove Tag',
27828     
27829             listeners : {
27830                 click : function ()
27831                 {
27832                     // remove
27833                     // undo does not work.
27834                      
27835                     var sn = tb.selectedNode;
27836                     
27837                     var pn = sn.parentNode;
27838                     
27839                     var stn =  sn.childNodes[0];
27840                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27841                     while (sn.childNodes.length) {
27842                         var node = sn.childNodes[0];
27843                         sn.removeChild(node);
27844                         //Roo.log(node);
27845                         pn.insertBefore(node, sn);
27846                         
27847                     }
27848                     pn.removeChild(sn);
27849                     var range = editorcore.createRange();
27850         
27851                     range.setStart(stn,0);
27852                     range.setEnd(en,0); //????
27853                     //range.selectNode(sel);
27854                     
27855                     
27856                     var selection = editorcore.getSelection();
27857                     selection.removeAllRanges();
27858                     selection.addRange(range);
27859                     
27860                     
27861                     
27862                     //_this.updateToolbar(null, null, pn);
27863                     _this.updateToolbar(null, null, null);
27864                     _this.footDisp.dom.innerHTML = ''; 
27865                 }
27866             }
27867             
27868                     
27869                 
27870             
27871         });
27872         
27873         
27874         tb.el.on('click', function(e){
27875             e.preventDefault(); // what does this do?
27876         });
27877         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27878         tb.el.hide();
27879         tb.name = nm;
27880         // dont need to disable them... as they will get hidden
27881         return tb;
27882          
27883         
27884     },
27885     buildFooter : function()
27886     {
27887         
27888         var fel = this.editor.wrap.createChild();
27889         this.footer = new Roo.Toolbar(fel);
27890         // toolbar has scrolly on left / right?
27891         var footDisp= new Roo.Toolbar.Fill();
27892         var _t = this;
27893         this.footer.add(
27894             {
27895                 text : '&lt;',
27896                 xtype: 'Button',
27897                 handler : function() {
27898                     _t.footDisp.scrollTo('left',0,true)
27899                 }
27900             }
27901         );
27902         this.footer.add( footDisp );
27903         this.footer.add( 
27904             {
27905                 text : '&gt;',
27906                 xtype: 'Button',
27907                 handler : function() {
27908                     // no animation..
27909                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27910                 }
27911             }
27912         );
27913         var fel = Roo.get(footDisp.el);
27914         fel.addClass('x-editor-context');
27915         this.footDispWrap = fel; 
27916         this.footDispWrap.overflow  = 'hidden';
27917         
27918         this.footDisp = fel.createChild();
27919         this.footDispWrap.on('click', this.onContextClick, this)
27920         
27921         
27922     },
27923     onContextClick : function (ev,dom)
27924     {
27925         ev.preventDefault();
27926         var  cn = dom.className;
27927         //Roo.log(cn);
27928         if (!cn.match(/x-ed-loc-/)) {
27929             return;
27930         }
27931         var n = cn.split('-').pop();
27932         var ans = this.footerEls;
27933         var sel = ans[n];
27934         
27935          // pick
27936         var range = this.editorcore.createRange();
27937         
27938         range.selectNodeContents(sel);
27939         //range.selectNode(sel);
27940         
27941         
27942         var selection = this.editorcore.getSelection();
27943         selection.removeAllRanges();
27944         selection.addRange(range);
27945         
27946         
27947         
27948         this.updateToolbar(null, null, sel);
27949         
27950         
27951     }
27952     
27953     
27954     
27955     
27956     
27957 });
27958
27959
27960
27961
27962
27963 /*
27964  * Based on:
27965  * Ext JS Library 1.1.1
27966  * Copyright(c) 2006-2007, Ext JS, LLC.
27967  *
27968  * Originally Released Under LGPL - original licence link has changed is not relivant.
27969  *
27970  * Fork - LGPL
27971  * <script type="text/javascript">
27972  */
27973  
27974 /**
27975  * @class Roo.form.BasicForm
27976  * @extends Roo.util.Observable
27977  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27978  * @constructor
27979  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27980  * @param {Object} config Configuration options
27981  */
27982 Roo.form.BasicForm = function(el, config){
27983     this.allItems = [];
27984     this.childForms = [];
27985     Roo.apply(this, config);
27986     /*
27987      * The Roo.form.Field items in this form.
27988      * @type MixedCollection
27989      */
27990      
27991      
27992     this.items = new Roo.util.MixedCollection(false, function(o){
27993         return o.id || (o.id = Roo.id());
27994     });
27995     this.addEvents({
27996         /**
27997          * @event beforeaction
27998          * Fires before any action is performed. Return false to cancel the action.
27999          * @param {Form} this
28000          * @param {Action} action The action to be performed
28001          */
28002         beforeaction: true,
28003         /**
28004          * @event actionfailed
28005          * Fires when an action fails.
28006          * @param {Form} this
28007          * @param {Action} action The action that failed
28008          */
28009         actionfailed : true,
28010         /**
28011          * @event actioncomplete
28012          * Fires when an action is completed.
28013          * @param {Form} this
28014          * @param {Action} action The action that completed
28015          */
28016         actioncomplete : true
28017     });
28018     if(el){
28019         this.initEl(el);
28020     }
28021     Roo.form.BasicForm.superclass.constructor.call(this);
28022 };
28023
28024 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28025     /**
28026      * @cfg {String} method
28027      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28028      */
28029     /**
28030      * @cfg {DataReader} reader
28031      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28032      * This is optional as there is built-in support for processing JSON.
28033      */
28034     /**
28035      * @cfg {DataReader} errorReader
28036      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28037      * This is completely optional as there is built-in support for processing JSON.
28038      */
28039     /**
28040      * @cfg {String} url
28041      * The URL to use for form actions if one isn't supplied in the action options.
28042      */
28043     /**
28044      * @cfg {Boolean} fileUpload
28045      * Set to true if this form is a file upload.
28046      */
28047      
28048     /**
28049      * @cfg {Object} baseParams
28050      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28051      */
28052      /**
28053      
28054     /**
28055      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28056      */
28057     timeout: 30,
28058
28059     // private
28060     activeAction : null,
28061
28062     /**
28063      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28064      * or setValues() data instead of when the form was first created.
28065      */
28066     trackResetOnLoad : false,
28067     
28068     
28069     /**
28070      * childForms - used for multi-tab forms
28071      * @type {Array}
28072      */
28073     childForms : false,
28074     
28075     /**
28076      * allItems - full list of fields.
28077      * @type {Array}
28078      */
28079     allItems : false,
28080     
28081     /**
28082      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28083      * element by passing it or its id or mask the form itself by passing in true.
28084      * @type Mixed
28085      */
28086     waitMsgTarget : false,
28087
28088     // private
28089     initEl : function(el){
28090         this.el = Roo.get(el);
28091         this.id = this.el.id || Roo.id();
28092         this.el.on('submit', this.onSubmit, this);
28093         this.el.addClass('x-form');
28094     },
28095
28096     // private
28097     onSubmit : function(e){
28098         e.stopEvent();
28099     },
28100
28101     /**
28102      * Returns true if client-side validation on the form is successful.
28103      * @return Boolean
28104      */
28105     isValid : function(){
28106         var valid = true;
28107         this.items.each(function(f){
28108            if(!f.validate()){
28109                valid = false;
28110            }
28111         });
28112         return valid;
28113     },
28114
28115     /**
28116      * Returns true if any fields in this form have changed since their original load.
28117      * @return Boolean
28118      */
28119     isDirty : function(){
28120         var dirty = false;
28121         this.items.each(function(f){
28122            if(f.isDirty()){
28123                dirty = true;
28124                return false;
28125            }
28126         });
28127         return dirty;
28128     },
28129
28130     /**
28131      * Performs a predefined action (submit or load) or custom actions you define on this form.
28132      * @param {String} actionName The name of the action type
28133      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28134      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28135      * accept other config options):
28136      * <pre>
28137 Property          Type             Description
28138 ----------------  ---------------  ----------------------------------------------------------------------------------
28139 url               String           The url for the action (defaults to the form's url)
28140 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28141 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28142 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28143                                    validate the form on the client (defaults to false)
28144      * </pre>
28145      * @return {BasicForm} this
28146      */
28147     doAction : function(action, options){
28148         if(typeof action == 'string'){
28149             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28150         }
28151         if(this.fireEvent('beforeaction', this, action) !== false){
28152             this.beforeAction(action);
28153             action.run.defer(100, action);
28154         }
28155         return this;
28156     },
28157
28158     /**
28159      * Shortcut to do a submit action.
28160      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28161      * @return {BasicForm} this
28162      */
28163     submit : function(options){
28164         this.doAction('submit', options);
28165         return this;
28166     },
28167
28168     /**
28169      * Shortcut to do a load action.
28170      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28171      * @return {BasicForm} this
28172      */
28173     load : function(options){
28174         this.doAction('load', options);
28175         return this;
28176     },
28177
28178     /**
28179      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28180      * @param {Record} record The record to edit
28181      * @return {BasicForm} this
28182      */
28183     updateRecord : function(record){
28184         record.beginEdit();
28185         var fs = record.fields;
28186         fs.each(function(f){
28187             var field = this.findField(f.name);
28188             if(field){
28189                 record.set(f.name, field.getValue());
28190             }
28191         }, this);
28192         record.endEdit();
28193         return this;
28194     },
28195
28196     /**
28197      * Loads an Roo.data.Record into this form.
28198      * @param {Record} record The record to load
28199      * @return {BasicForm} this
28200      */
28201     loadRecord : function(record){
28202         this.setValues(record.data);
28203         return this;
28204     },
28205
28206     // private
28207     beforeAction : function(action){
28208         var o = action.options;
28209         
28210        
28211         if(this.waitMsgTarget === true){
28212             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28213         }else if(this.waitMsgTarget){
28214             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28215             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28216         }else {
28217             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28218         }
28219          
28220     },
28221
28222     // private
28223     afterAction : function(action, success){
28224         this.activeAction = null;
28225         var o = action.options;
28226         
28227         if(this.waitMsgTarget === true){
28228             this.el.unmask();
28229         }else if(this.waitMsgTarget){
28230             this.waitMsgTarget.unmask();
28231         }else{
28232             Roo.MessageBox.updateProgress(1);
28233             Roo.MessageBox.hide();
28234         }
28235          
28236         if(success){
28237             if(o.reset){
28238                 this.reset();
28239             }
28240             Roo.callback(o.success, o.scope, [this, action]);
28241             this.fireEvent('actioncomplete', this, action);
28242             
28243         }else{
28244             
28245             // failure condition..
28246             // we have a scenario where updates need confirming.
28247             // eg. if a locking scenario exists..
28248             // we look for { errors : { needs_confirm : true }} in the response.
28249             if (
28250                 (typeof(action.result) != 'undefined')  &&
28251                 (typeof(action.result.errors) != 'undefined')  &&
28252                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28253            ){
28254                 var _t = this;
28255                 Roo.MessageBox.confirm(
28256                     "Change requires confirmation",
28257                     action.result.errorMsg,
28258                     function(r) {
28259                         if (r != 'yes') {
28260                             return;
28261                         }
28262                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28263                     }
28264                     
28265                 );
28266                 
28267                 
28268                 
28269                 return;
28270             }
28271             
28272             Roo.callback(o.failure, o.scope, [this, action]);
28273             // show an error message if no failed handler is set..
28274             if (!this.hasListener('actionfailed')) {
28275                 Roo.MessageBox.alert("Error",
28276                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28277                         action.result.errorMsg :
28278                         "Saving Failed, please check your entries or try again"
28279                 );
28280             }
28281             
28282             this.fireEvent('actionfailed', this, action);
28283         }
28284         
28285     },
28286
28287     /**
28288      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28289      * @param {String} id The value to search for
28290      * @return Field
28291      */
28292     findField : function(id){
28293         var field = this.items.get(id);
28294         if(!field){
28295             this.items.each(function(f){
28296                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28297                     field = f;
28298                     return false;
28299                 }
28300             });
28301         }
28302         return field || null;
28303     },
28304
28305     /**
28306      * Add a secondary form to this one, 
28307      * Used to provide tabbed forms. One form is primary, with hidden values 
28308      * which mirror the elements from the other forms.
28309      * 
28310      * @param {Roo.form.Form} form to add.
28311      * 
28312      */
28313     addForm : function(form)
28314     {
28315        
28316         if (this.childForms.indexOf(form) > -1) {
28317             // already added..
28318             return;
28319         }
28320         this.childForms.push(form);
28321         var n = '';
28322         Roo.each(form.allItems, function (fe) {
28323             
28324             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28325             if (this.findField(n)) { // already added..
28326                 return;
28327             }
28328             var add = new Roo.form.Hidden({
28329                 name : n
28330             });
28331             add.render(this.el);
28332             
28333             this.add( add );
28334         }, this);
28335         
28336     },
28337     /**
28338      * Mark fields in this form invalid in bulk.
28339      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28340      * @return {BasicForm} this
28341      */
28342     markInvalid : function(errors){
28343         if(errors instanceof Array){
28344             for(var i = 0, len = errors.length; i < len; i++){
28345                 var fieldError = errors[i];
28346                 var f = this.findField(fieldError.id);
28347                 if(f){
28348                     f.markInvalid(fieldError.msg);
28349                 }
28350             }
28351         }else{
28352             var field, id;
28353             for(id in errors){
28354                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28355                     field.markInvalid(errors[id]);
28356                 }
28357             }
28358         }
28359         Roo.each(this.childForms || [], function (f) {
28360             f.markInvalid(errors);
28361         });
28362         
28363         return this;
28364     },
28365
28366     /**
28367      * Set values for fields in this form in bulk.
28368      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28369      * @return {BasicForm} this
28370      */
28371     setValues : function(values){
28372         if(values instanceof Array){ // array of objects
28373             for(var i = 0, len = values.length; i < len; i++){
28374                 var v = values[i];
28375                 var f = this.findField(v.id);
28376                 if(f){
28377                     f.setValue(v.value);
28378                     if(this.trackResetOnLoad){
28379                         f.originalValue = f.getValue();
28380                     }
28381                 }
28382             }
28383         }else{ // object hash
28384             var field, id;
28385             for(id in values){
28386                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28387                     
28388                     if (field.setFromData && 
28389                         field.valueField && 
28390                         field.displayField &&
28391                         // combos' with local stores can 
28392                         // be queried via setValue()
28393                         // to set their value..
28394                         (field.store && !field.store.isLocal)
28395                         ) {
28396                         // it's a combo
28397                         var sd = { };
28398                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28399                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28400                         field.setFromData(sd);
28401                         
28402                     } else {
28403                         field.setValue(values[id]);
28404                     }
28405                     
28406                     
28407                     if(this.trackResetOnLoad){
28408                         field.originalValue = field.getValue();
28409                     }
28410                 }
28411             }
28412         }
28413          
28414         Roo.each(this.childForms || [], function (f) {
28415             f.setValues(values);
28416         });
28417                 
28418         return this;
28419     },
28420
28421     /**
28422      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28423      * they are returned as an array.
28424      * @param {Boolean} asString
28425      * @return {Object}
28426      */
28427     getValues : function(asString){
28428         if (this.childForms) {
28429             // copy values from the child forms
28430             Roo.each(this.childForms, function (f) {
28431                 this.setValues(f.getValues());
28432             }, this);
28433         }
28434         
28435         
28436         
28437         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28438         if(asString === true){
28439             return fs;
28440         }
28441         return Roo.urlDecode(fs);
28442     },
28443     
28444     /**
28445      * Returns the fields in this form as an object with key/value pairs. 
28446      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28447      * @return {Object}
28448      */
28449     getFieldValues : function(with_hidden)
28450     {
28451         if (this.childForms) {
28452             // copy values from the child forms
28453             // should this call getFieldValues - probably not as we do not currently copy
28454             // hidden fields when we generate..
28455             Roo.each(this.childForms, function (f) {
28456                 this.setValues(f.getValues());
28457             }, this);
28458         }
28459         
28460         var ret = {};
28461         this.items.each(function(f){
28462             if (!f.getName()) {
28463                 return;
28464             }
28465             var v = f.getValue();
28466             if (f.inputType =='radio') {
28467                 if (typeof(ret[f.getName()]) == 'undefined') {
28468                     ret[f.getName()] = ''; // empty..
28469                 }
28470                 
28471                 if (!f.el.dom.checked) {
28472                     return;
28473                     
28474                 }
28475                 v = f.el.dom.value;
28476                 
28477             }
28478             
28479             // not sure if this supported any more..
28480             if ((typeof(v) == 'object') && f.getRawValue) {
28481                 v = f.getRawValue() ; // dates..
28482             }
28483             // combo boxes where name != hiddenName...
28484             if (f.name != f.getName()) {
28485                 ret[f.name] = f.getRawValue();
28486             }
28487             ret[f.getName()] = v;
28488         });
28489         
28490         return ret;
28491     },
28492
28493     /**
28494      * Clears all invalid messages in this form.
28495      * @return {BasicForm} this
28496      */
28497     clearInvalid : function(){
28498         this.items.each(function(f){
28499            f.clearInvalid();
28500         });
28501         
28502         Roo.each(this.childForms || [], function (f) {
28503             f.clearInvalid();
28504         });
28505         
28506         
28507         return this;
28508     },
28509
28510     /**
28511      * Resets this form.
28512      * @return {BasicForm} this
28513      */
28514     reset : function(){
28515         this.items.each(function(f){
28516             f.reset();
28517         });
28518         
28519         Roo.each(this.childForms || [], function (f) {
28520             f.reset();
28521         });
28522        
28523         
28524         return this;
28525     },
28526
28527     /**
28528      * Add Roo.form components to this form.
28529      * @param {Field} field1
28530      * @param {Field} field2 (optional)
28531      * @param {Field} etc (optional)
28532      * @return {BasicForm} this
28533      */
28534     add : function(){
28535         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28536         return this;
28537     },
28538
28539
28540     /**
28541      * Removes a field from the items collection (does NOT remove its markup).
28542      * @param {Field} field
28543      * @return {BasicForm} this
28544      */
28545     remove : function(field){
28546         this.items.remove(field);
28547         return this;
28548     },
28549
28550     /**
28551      * Looks at the fields in this form, checks them for an id attribute,
28552      * and calls applyTo on the existing dom element with that id.
28553      * @return {BasicForm} this
28554      */
28555     render : function(){
28556         this.items.each(function(f){
28557             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28558                 f.applyTo(f.id);
28559             }
28560         });
28561         return this;
28562     },
28563
28564     /**
28565      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28566      * @param {Object} values
28567      * @return {BasicForm} this
28568      */
28569     applyToFields : function(o){
28570         this.items.each(function(f){
28571            Roo.apply(f, o);
28572         });
28573         return this;
28574     },
28575
28576     /**
28577      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28578      * @param {Object} values
28579      * @return {BasicForm} this
28580      */
28581     applyIfToFields : function(o){
28582         this.items.each(function(f){
28583            Roo.applyIf(f, o);
28584         });
28585         return this;
28586     }
28587 });
28588
28589 // back compat
28590 Roo.BasicForm = Roo.form.BasicForm;/*
28591  * Based on:
28592  * Ext JS Library 1.1.1
28593  * Copyright(c) 2006-2007, Ext JS, LLC.
28594  *
28595  * Originally Released Under LGPL - original licence link has changed is not relivant.
28596  *
28597  * Fork - LGPL
28598  * <script type="text/javascript">
28599  */
28600
28601 /**
28602  * @class Roo.form.Form
28603  * @extends Roo.form.BasicForm
28604  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28605  * @constructor
28606  * @param {Object} config Configuration options
28607  */
28608 Roo.form.Form = function(config){
28609     var xitems =  [];
28610     if (config.items) {
28611         xitems = config.items;
28612         delete config.items;
28613     }
28614    
28615     
28616     Roo.form.Form.superclass.constructor.call(this, null, config);
28617     this.url = this.url || this.action;
28618     if(!this.root){
28619         this.root = new Roo.form.Layout(Roo.applyIf({
28620             id: Roo.id()
28621         }, config));
28622     }
28623     this.active = this.root;
28624     /**
28625      * Array of all the buttons that have been added to this form via {@link addButton}
28626      * @type Array
28627      */
28628     this.buttons = [];
28629     this.allItems = [];
28630     this.addEvents({
28631         /**
28632          * @event clientvalidation
28633          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28634          * @param {Form} this
28635          * @param {Boolean} valid true if the form has passed client-side validation
28636          */
28637         clientvalidation: true,
28638         /**
28639          * @event rendered
28640          * Fires when the form is rendered
28641          * @param {Roo.form.Form} form
28642          */
28643         rendered : true
28644     });
28645     
28646     if (this.progressUrl) {
28647             // push a hidden field onto the list of fields..
28648             this.addxtype( {
28649                     xns: Roo.form, 
28650                     xtype : 'Hidden', 
28651                     name : 'UPLOAD_IDENTIFIER' 
28652             });
28653         }
28654         
28655     
28656     Roo.each(xitems, this.addxtype, this);
28657     
28658     
28659     
28660 };
28661
28662 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28663     /**
28664      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28665      */
28666     /**
28667      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28668      */
28669     /**
28670      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28671      */
28672     buttonAlign:'center',
28673
28674     /**
28675      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28676      */
28677     minButtonWidth:75,
28678
28679     /**
28680      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28681      * This property cascades to child containers if not set.
28682      */
28683     labelAlign:'left',
28684
28685     /**
28686      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28687      * fires a looping event with that state. This is required to bind buttons to the valid
28688      * state using the config value formBind:true on the button.
28689      */
28690     monitorValid : false,
28691
28692     /**
28693      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28694      */
28695     monitorPoll : 200,
28696     
28697     /**
28698      * @cfg {String} progressUrl - Url to return progress data 
28699      */
28700     
28701     progressUrl : false,
28702   
28703     /**
28704      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28705      * fields are added and the column is closed. If no fields are passed the column remains open
28706      * until end() is called.
28707      * @param {Object} config The config to pass to the column
28708      * @param {Field} field1 (optional)
28709      * @param {Field} field2 (optional)
28710      * @param {Field} etc (optional)
28711      * @return Column The column container object
28712      */
28713     column : function(c){
28714         var col = new Roo.form.Column(c);
28715         this.start(col);
28716         if(arguments.length > 1){ // duplicate code required because of Opera
28717             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28718             this.end();
28719         }
28720         return col;
28721     },
28722
28723     /**
28724      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28725      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28726      * until end() is called.
28727      * @param {Object} config The config to pass to the fieldset
28728      * @param {Field} field1 (optional)
28729      * @param {Field} field2 (optional)
28730      * @param {Field} etc (optional)
28731      * @return FieldSet The fieldset container object
28732      */
28733     fieldset : function(c){
28734         var fs = new Roo.form.FieldSet(c);
28735         this.start(fs);
28736         if(arguments.length > 1){ // duplicate code required because of Opera
28737             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28738             this.end();
28739         }
28740         return fs;
28741     },
28742
28743     /**
28744      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28745      * fields are added and the container is closed. If no fields are passed the container remains open
28746      * until end() is called.
28747      * @param {Object} config The config to pass to the Layout
28748      * @param {Field} field1 (optional)
28749      * @param {Field} field2 (optional)
28750      * @param {Field} etc (optional)
28751      * @return Layout The container object
28752      */
28753     container : function(c){
28754         var l = new Roo.form.Layout(c);
28755         this.start(l);
28756         if(arguments.length > 1){ // duplicate code required because of Opera
28757             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28758             this.end();
28759         }
28760         return l;
28761     },
28762
28763     /**
28764      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28765      * @param {Object} container A Roo.form.Layout or subclass of Layout
28766      * @return {Form} this
28767      */
28768     start : function(c){
28769         // cascade label info
28770         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28771         this.active.stack.push(c);
28772         c.ownerCt = this.active;
28773         this.active = c;
28774         return this;
28775     },
28776
28777     /**
28778      * Closes the current open container
28779      * @return {Form} this
28780      */
28781     end : function(){
28782         if(this.active == this.root){
28783             return this;
28784         }
28785         this.active = this.active.ownerCt;
28786         return this;
28787     },
28788
28789     /**
28790      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28791      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28792      * as the label of the field.
28793      * @param {Field} field1
28794      * @param {Field} field2 (optional)
28795      * @param {Field} etc. (optional)
28796      * @return {Form} this
28797      */
28798     add : function(){
28799         this.active.stack.push.apply(this.active.stack, arguments);
28800         this.allItems.push.apply(this.allItems,arguments);
28801         var r = [];
28802         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28803             if(a[i].isFormField){
28804                 r.push(a[i]);
28805             }
28806         }
28807         if(r.length > 0){
28808             Roo.form.Form.superclass.add.apply(this, r);
28809         }
28810         return this;
28811     },
28812     
28813
28814     
28815     
28816     
28817      /**
28818      * Find any element that has been added to a form, using it's ID or name
28819      * This can include framesets, columns etc. along with regular fields..
28820      * @param {String} id - id or name to find.
28821      
28822      * @return {Element} e - or false if nothing found.
28823      */
28824     findbyId : function(id)
28825     {
28826         var ret = false;
28827         if (!id) {
28828             return ret;
28829         }
28830         Roo.each(this.allItems, function(f){
28831             if (f.id == id || f.name == id ){
28832                 ret = f;
28833                 return false;
28834             }
28835         });
28836         return ret;
28837     },
28838
28839     
28840     
28841     /**
28842      * Render this form into the passed container. This should only be called once!
28843      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28844      * @return {Form} this
28845      */
28846     render : function(ct)
28847     {
28848         
28849         
28850         
28851         ct = Roo.get(ct);
28852         var o = this.autoCreate || {
28853             tag: 'form',
28854             method : this.method || 'POST',
28855             id : this.id || Roo.id()
28856         };
28857         this.initEl(ct.createChild(o));
28858
28859         this.root.render(this.el);
28860         
28861        
28862              
28863         this.items.each(function(f){
28864             f.render('x-form-el-'+f.id);
28865         });
28866
28867         if(this.buttons.length > 0){
28868             // tables are required to maintain order and for correct IE layout
28869             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28870                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28871                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28872             }}, null, true);
28873             var tr = tb.getElementsByTagName('tr')[0];
28874             for(var i = 0, len = this.buttons.length; i < len; i++) {
28875                 var b = this.buttons[i];
28876                 var td = document.createElement('td');
28877                 td.className = 'x-form-btn-td';
28878                 b.render(tr.appendChild(td));
28879             }
28880         }
28881         if(this.monitorValid){ // initialize after render
28882             this.startMonitoring();
28883         }
28884         this.fireEvent('rendered', this);
28885         return this;
28886     },
28887
28888     /**
28889      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28890      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28891      * object or a valid Roo.DomHelper element config
28892      * @param {Function} handler The function called when the button is clicked
28893      * @param {Object} scope (optional) The scope of the handler function
28894      * @return {Roo.Button}
28895      */
28896     addButton : function(config, handler, scope){
28897         var bc = {
28898             handler: handler,
28899             scope: scope,
28900             minWidth: this.minButtonWidth,
28901             hideParent:true
28902         };
28903         if(typeof config == "string"){
28904             bc.text = config;
28905         }else{
28906             Roo.apply(bc, config);
28907         }
28908         var btn = new Roo.Button(null, bc);
28909         this.buttons.push(btn);
28910         return btn;
28911     },
28912
28913      /**
28914      * Adds a series of form elements (using the xtype property as the factory method.
28915      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28916      * @param {Object} config 
28917      */
28918     
28919     addxtype : function()
28920     {
28921         var ar = Array.prototype.slice.call(arguments, 0);
28922         var ret = false;
28923         for(var i = 0; i < ar.length; i++) {
28924             if (!ar[i]) {
28925                 continue; // skip -- if this happends something invalid got sent, we 
28926                 // should ignore it, as basically that interface element will not show up
28927                 // and that should be pretty obvious!!
28928             }
28929             
28930             if (Roo.form[ar[i].xtype]) {
28931                 ar[i].form = this;
28932                 var fe = Roo.factory(ar[i], Roo.form);
28933                 if (!ret) {
28934                     ret = fe;
28935                 }
28936                 fe.form = this;
28937                 if (fe.store) {
28938                     fe.store.form = this;
28939                 }
28940                 if (fe.isLayout) {  
28941                          
28942                     this.start(fe);
28943                     this.allItems.push(fe);
28944                     if (fe.items && fe.addxtype) {
28945                         fe.addxtype.apply(fe, fe.items);
28946                         delete fe.items;
28947                     }
28948                      this.end();
28949                     continue;
28950                 }
28951                 
28952                 
28953                  
28954                 this.add(fe);
28955               //  console.log('adding ' + ar[i].xtype);
28956             }
28957             if (ar[i].xtype == 'Button') {  
28958                 //console.log('adding button');
28959                 //console.log(ar[i]);
28960                 this.addButton(ar[i]);
28961                 this.allItems.push(fe);
28962                 continue;
28963             }
28964             
28965             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28966                 alert('end is not supported on xtype any more, use items');
28967             //    this.end();
28968             //    //console.log('adding end');
28969             }
28970             
28971         }
28972         return ret;
28973     },
28974     
28975     /**
28976      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28977      * option "monitorValid"
28978      */
28979     startMonitoring : function(){
28980         if(!this.bound){
28981             this.bound = true;
28982             Roo.TaskMgr.start({
28983                 run : this.bindHandler,
28984                 interval : this.monitorPoll || 200,
28985                 scope: this
28986             });
28987         }
28988     },
28989
28990     /**
28991      * Stops monitoring of the valid state of this form
28992      */
28993     stopMonitoring : function(){
28994         this.bound = false;
28995     },
28996
28997     // private
28998     bindHandler : function(){
28999         if(!this.bound){
29000             return false; // stops binding
29001         }
29002         var valid = true;
29003         this.items.each(function(f){
29004             if(!f.isValid(true)){
29005                 valid = false;
29006                 return false;
29007             }
29008         });
29009         for(var i = 0, len = this.buttons.length; i < len; i++){
29010             var btn = this.buttons[i];
29011             if(btn.formBind === true && btn.disabled === valid){
29012                 btn.setDisabled(!valid);
29013             }
29014         }
29015         this.fireEvent('clientvalidation', this, valid);
29016     }
29017     
29018     
29019     
29020     
29021     
29022     
29023     
29024     
29025 });
29026
29027
29028 // back compat
29029 Roo.Form = Roo.form.Form;
29030 /*
29031  * Based on:
29032  * Ext JS Library 1.1.1
29033  * Copyright(c) 2006-2007, Ext JS, LLC.
29034  *
29035  * Originally Released Under LGPL - original licence link has changed is not relivant.
29036  *
29037  * Fork - LGPL
29038  * <script type="text/javascript">
29039  */
29040
29041 // as we use this in bootstrap.
29042 Roo.namespace('Roo.form');
29043  /**
29044  * @class Roo.form.Action
29045  * Internal Class used to handle form actions
29046  * @constructor
29047  * @param {Roo.form.BasicForm} el The form element or its id
29048  * @param {Object} config Configuration options
29049  */
29050
29051  
29052  
29053 // define the action interface
29054 Roo.form.Action = function(form, options){
29055     this.form = form;
29056     this.options = options || {};
29057 };
29058 /**
29059  * Client Validation Failed
29060  * @const 
29061  */
29062 Roo.form.Action.CLIENT_INVALID = 'client';
29063 /**
29064  * Server Validation Failed
29065  * @const 
29066  */
29067 Roo.form.Action.SERVER_INVALID = 'server';
29068  /**
29069  * Connect to Server Failed
29070  * @const 
29071  */
29072 Roo.form.Action.CONNECT_FAILURE = 'connect';
29073 /**
29074  * Reading Data from Server Failed
29075  * @const 
29076  */
29077 Roo.form.Action.LOAD_FAILURE = 'load';
29078
29079 Roo.form.Action.prototype = {
29080     type : 'default',
29081     failureType : undefined,
29082     response : undefined,
29083     result : undefined,
29084
29085     // interface method
29086     run : function(options){
29087
29088     },
29089
29090     // interface method
29091     success : function(response){
29092
29093     },
29094
29095     // interface method
29096     handleResponse : function(response){
29097
29098     },
29099
29100     // default connection failure
29101     failure : function(response){
29102         
29103         this.response = response;
29104         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29105         this.form.afterAction(this, false);
29106     },
29107
29108     processResponse : function(response){
29109         this.response = response;
29110         if(!response.responseText){
29111             return true;
29112         }
29113         this.result = this.handleResponse(response);
29114         return this.result;
29115     },
29116
29117     // utility functions used internally
29118     getUrl : function(appendParams){
29119         var url = this.options.url || this.form.url || this.form.el.dom.action;
29120         if(appendParams){
29121             var p = this.getParams();
29122             if(p){
29123                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29124             }
29125         }
29126         return url;
29127     },
29128
29129     getMethod : function(){
29130         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29131     },
29132
29133     getParams : function(){
29134         var bp = this.form.baseParams;
29135         var p = this.options.params;
29136         if(p){
29137             if(typeof p == "object"){
29138                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29139             }else if(typeof p == 'string' && bp){
29140                 p += '&' + Roo.urlEncode(bp);
29141             }
29142         }else if(bp){
29143             p = Roo.urlEncode(bp);
29144         }
29145         return p;
29146     },
29147
29148     createCallback : function(){
29149         return {
29150             success: this.success,
29151             failure: this.failure,
29152             scope: this,
29153             timeout: (this.form.timeout*1000),
29154             upload: this.form.fileUpload ? this.success : undefined
29155         };
29156     }
29157 };
29158
29159 Roo.form.Action.Submit = function(form, options){
29160     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29161 };
29162
29163 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29164     type : 'submit',
29165
29166     haveProgress : false,
29167     uploadComplete : false,
29168     
29169     // uploadProgress indicator.
29170     uploadProgress : function()
29171     {
29172         if (!this.form.progressUrl) {
29173             return;
29174         }
29175         
29176         if (!this.haveProgress) {
29177             Roo.MessageBox.progress("Uploading", "Uploading");
29178         }
29179         if (this.uploadComplete) {
29180            Roo.MessageBox.hide();
29181            return;
29182         }
29183         
29184         this.haveProgress = true;
29185    
29186         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29187         
29188         var c = new Roo.data.Connection();
29189         c.request({
29190             url : this.form.progressUrl,
29191             params: {
29192                 id : uid
29193             },
29194             method: 'GET',
29195             success : function(req){
29196                //console.log(data);
29197                 var rdata = false;
29198                 var edata;
29199                 try  {
29200                    rdata = Roo.decode(req.responseText)
29201                 } catch (e) {
29202                     Roo.log("Invalid data from server..");
29203                     Roo.log(edata);
29204                     return;
29205                 }
29206                 if (!rdata || !rdata.success) {
29207                     Roo.log(rdata);
29208                     Roo.MessageBox.alert(Roo.encode(rdata));
29209                     return;
29210                 }
29211                 var data = rdata.data;
29212                 
29213                 if (this.uploadComplete) {
29214                    Roo.MessageBox.hide();
29215                    return;
29216                 }
29217                    
29218                 if (data){
29219                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29220                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29221                     );
29222                 }
29223                 this.uploadProgress.defer(2000,this);
29224             },
29225        
29226             failure: function(data) {
29227                 Roo.log('progress url failed ');
29228                 Roo.log(data);
29229             },
29230             scope : this
29231         });
29232            
29233     },
29234     
29235     
29236     run : function()
29237     {
29238         // run get Values on the form, so it syncs any secondary forms.
29239         this.form.getValues();
29240         
29241         var o = this.options;
29242         var method = this.getMethod();
29243         var isPost = method == 'POST';
29244         if(o.clientValidation === false || this.form.isValid()){
29245             
29246             if (this.form.progressUrl) {
29247                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29248                     (new Date() * 1) + '' + Math.random());
29249                     
29250             } 
29251             
29252             
29253             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29254                 form:this.form.el.dom,
29255                 url:this.getUrl(!isPost),
29256                 method: method,
29257                 params:isPost ? this.getParams() : null,
29258                 isUpload: this.form.fileUpload
29259             }));
29260             
29261             this.uploadProgress();
29262
29263         }else if (o.clientValidation !== false){ // client validation failed
29264             this.failureType = Roo.form.Action.CLIENT_INVALID;
29265             this.form.afterAction(this, false);
29266         }
29267     },
29268
29269     success : function(response)
29270     {
29271         this.uploadComplete= true;
29272         if (this.haveProgress) {
29273             Roo.MessageBox.hide();
29274         }
29275         
29276         
29277         var result = this.processResponse(response);
29278         if(result === true || result.success){
29279             this.form.afterAction(this, true);
29280             return;
29281         }
29282         if(result.errors){
29283             this.form.markInvalid(result.errors);
29284             this.failureType = Roo.form.Action.SERVER_INVALID;
29285         }
29286         this.form.afterAction(this, false);
29287     },
29288     failure : function(response)
29289     {
29290         this.uploadComplete= true;
29291         if (this.haveProgress) {
29292             Roo.MessageBox.hide();
29293         }
29294         
29295         this.response = response;
29296         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29297         this.form.afterAction(this, false);
29298     },
29299     
29300     handleResponse : function(response){
29301         if(this.form.errorReader){
29302             var rs = this.form.errorReader.read(response);
29303             var errors = [];
29304             if(rs.records){
29305                 for(var i = 0, len = rs.records.length; i < len; i++) {
29306                     var r = rs.records[i];
29307                     errors[i] = r.data;
29308                 }
29309             }
29310             if(errors.length < 1){
29311                 errors = null;
29312             }
29313             return {
29314                 success : rs.success,
29315                 errors : errors
29316             };
29317         }
29318         var ret = false;
29319         try {
29320             ret = Roo.decode(response.responseText);
29321         } catch (e) {
29322             ret = {
29323                 success: false,
29324                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29325                 errors : []
29326             };
29327         }
29328         return ret;
29329         
29330     }
29331 });
29332
29333
29334 Roo.form.Action.Load = function(form, options){
29335     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29336     this.reader = this.form.reader;
29337 };
29338
29339 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29340     type : 'load',
29341
29342     run : function(){
29343         
29344         Roo.Ajax.request(Roo.apply(
29345                 this.createCallback(), {
29346                     method:this.getMethod(),
29347                     url:this.getUrl(false),
29348                     params:this.getParams()
29349         }));
29350     },
29351
29352     success : function(response){
29353         
29354         var result = this.processResponse(response);
29355         if(result === true || !result.success || !result.data){
29356             this.failureType = Roo.form.Action.LOAD_FAILURE;
29357             this.form.afterAction(this, false);
29358             return;
29359         }
29360         this.form.clearInvalid();
29361         this.form.setValues(result.data);
29362         this.form.afterAction(this, true);
29363     },
29364
29365     handleResponse : function(response){
29366         if(this.form.reader){
29367             var rs = this.form.reader.read(response);
29368             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29369             return {
29370                 success : rs.success,
29371                 data : data
29372             };
29373         }
29374         return Roo.decode(response.responseText);
29375     }
29376 });
29377
29378 Roo.form.Action.ACTION_TYPES = {
29379     'load' : Roo.form.Action.Load,
29380     'submit' : Roo.form.Action.Submit
29381 };/*
29382  * Based on:
29383  * Ext JS Library 1.1.1
29384  * Copyright(c) 2006-2007, Ext JS, LLC.
29385  *
29386  * Originally Released Under LGPL - original licence link has changed is not relivant.
29387  *
29388  * Fork - LGPL
29389  * <script type="text/javascript">
29390  */
29391  
29392 /**
29393  * @class Roo.form.Layout
29394  * @extends Roo.Component
29395  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29396  * @constructor
29397  * @param {Object} config Configuration options
29398  */
29399 Roo.form.Layout = function(config){
29400     var xitems = [];
29401     if (config.items) {
29402         xitems = config.items;
29403         delete config.items;
29404     }
29405     Roo.form.Layout.superclass.constructor.call(this, config);
29406     this.stack = [];
29407     Roo.each(xitems, this.addxtype, this);
29408      
29409 };
29410
29411 Roo.extend(Roo.form.Layout, Roo.Component, {
29412     /**
29413      * @cfg {String/Object} autoCreate
29414      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29415      */
29416     /**
29417      * @cfg {String/Object/Function} style
29418      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29419      * a function which returns such a specification.
29420      */
29421     /**
29422      * @cfg {String} labelAlign
29423      * Valid values are "left," "top" and "right" (defaults to "left")
29424      */
29425     /**
29426      * @cfg {Number} labelWidth
29427      * Fixed width in pixels of all field labels (defaults to undefined)
29428      */
29429     /**
29430      * @cfg {Boolean} clear
29431      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29432      */
29433     clear : true,
29434     /**
29435      * @cfg {String} labelSeparator
29436      * The separator to use after field labels (defaults to ':')
29437      */
29438     labelSeparator : ':',
29439     /**
29440      * @cfg {Boolean} hideLabels
29441      * True to suppress the display of field labels in this layout (defaults to false)
29442      */
29443     hideLabels : false,
29444
29445     // private
29446     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29447     
29448     isLayout : true,
29449     
29450     // private
29451     onRender : function(ct, position){
29452         if(this.el){ // from markup
29453             this.el = Roo.get(this.el);
29454         }else {  // generate
29455             var cfg = this.getAutoCreate();
29456             this.el = ct.createChild(cfg, position);
29457         }
29458         if(this.style){
29459             this.el.applyStyles(this.style);
29460         }
29461         if(this.labelAlign){
29462             this.el.addClass('x-form-label-'+this.labelAlign);
29463         }
29464         if(this.hideLabels){
29465             this.labelStyle = "display:none";
29466             this.elementStyle = "padding-left:0;";
29467         }else{
29468             if(typeof this.labelWidth == 'number'){
29469                 this.labelStyle = "width:"+this.labelWidth+"px;";
29470                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29471             }
29472             if(this.labelAlign == 'top'){
29473                 this.labelStyle = "width:auto;";
29474                 this.elementStyle = "padding-left:0;";
29475             }
29476         }
29477         var stack = this.stack;
29478         var slen = stack.length;
29479         if(slen > 0){
29480             if(!this.fieldTpl){
29481                 var t = new Roo.Template(
29482                     '<div class="x-form-item {5}">',
29483                         '<label for="{0}" style="{2}">{1}{4}</label>',
29484                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29485                         '</div>',
29486                     '</div><div class="x-form-clear-left"></div>'
29487                 );
29488                 t.disableFormats = true;
29489                 t.compile();
29490                 Roo.form.Layout.prototype.fieldTpl = t;
29491             }
29492             for(var i = 0; i < slen; i++) {
29493                 if(stack[i].isFormField){
29494                     this.renderField(stack[i]);
29495                 }else{
29496                     this.renderComponent(stack[i]);
29497                 }
29498             }
29499         }
29500         if(this.clear){
29501             this.el.createChild({cls:'x-form-clear'});
29502         }
29503     },
29504
29505     // private
29506     renderField : function(f){
29507         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29508                f.id, //0
29509                f.fieldLabel, //1
29510                f.labelStyle||this.labelStyle||'', //2
29511                this.elementStyle||'', //3
29512                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29513                f.itemCls||this.itemCls||''  //5
29514        ], true).getPrevSibling());
29515     },
29516
29517     // private
29518     renderComponent : function(c){
29519         c.render(c.isLayout ? this.el : this.el.createChild());    
29520     },
29521     /**
29522      * Adds a object form elements (using the xtype property as the factory method.)
29523      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29524      * @param {Object} config 
29525      */
29526     addxtype : function(o)
29527     {
29528         // create the lement.
29529         o.form = this.form;
29530         var fe = Roo.factory(o, Roo.form);
29531         this.form.allItems.push(fe);
29532         this.stack.push(fe);
29533         
29534         if (fe.isFormField) {
29535             this.form.items.add(fe);
29536         }
29537          
29538         return fe;
29539     }
29540 });
29541
29542 /**
29543  * @class Roo.form.Column
29544  * @extends Roo.form.Layout
29545  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29546  * @constructor
29547  * @param {Object} config Configuration options
29548  */
29549 Roo.form.Column = function(config){
29550     Roo.form.Column.superclass.constructor.call(this, config);
29551 };
29552
29553 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29554     /**
29555      * @cfg {Number/String} width
29556      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29557      */
29558     /**
29559      * @cfg {String/Object} autoCreate
29560      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29561      */
29562
29563     // private
29564     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29565
29566     // private
29567     onRender : function(ct, position){
29568         Roo.form.Column.superclass.onRender.call(this, ct, position);
29569         if(this.width){
29570             this.el.setWidth(this.width);
29571         }
29572     }
29573 });
29574
29575
29576 /**
29577  * @class Roo.form.Row
29578  * @extends Roo.form.Layout
29579  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29580  * @constructor
29581  * @param {Object} config Configuration options
29582  */
29583
29584  
29585 Roo.form.Row = function(config){
29586     Roo.form.Row.superclass.constructor.call(this, config);
29587 };
29588  
29589 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29590       /**
29591      * @cfg {Number/String} width
29592      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29593      */
29594     /**
29595      * @cfg {Number/String} height
29596      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29597      */
29598     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29599     
29600     padWidth : 20,
29601     // private
29602     onRender : function(ct, position){
29603         //console.log('row render');
29604         if(!this.rowTpl){
29605             var t = new Roo.Template(
29606                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29607                     '<label for="{0}" style="{2}">{1}{4}</label>',
29608                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29609                     '</div>',
29610                 '</div>'
29611             );
29612             t.disableFormats = true;
29613             t.compile();
29614             Roo.form.Layout.prototype.rowTpl = t;
29615         }
29616         this.fieldTpl = this.rowTpl;
29617         
29618         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29619         var labelWidth = 100;
29620         
29621         if ((this.labelAlign != 'top')) {
29622             if (typeof this.labelWidth == 'number') {
29623                 labelWidth = this.labelWidth
29624             }
29625             this.padWidth =  20 + labelWidth;
29626             
29627         }
29628         
29629         Roo.form.Column.superclass.onRender.call(this, ct, position);
29630         if(this.width){
29631             this.el.setWidth(this.width);
29632         }
29633         if(this.height){
29634             this.el.setHeight(this.height);
29635         }
29636     },
29637     
29638     // private
29639     renderField : function(f){
29640         f.fieldEl = this.fieldTpl.append(this.el, [
29641                f.id, f.fieldLabel,
29642                f.labelStyle||this.labelStyle||'',
29643                this.elementStyle||'',
29644                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29645                f.itemCls||this.itemCls||'',
29646                f.width ? f.width + this.padWidth : 160 + this.padWidth
29647        ],true);
29648     }
29649 });
29650  
29651
29652 /**
29653  * @class Roo.form.FieldSet
29654  * @extends Roo.form.Layout
29655  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29656  * @constructor
29657  * @param {Object} config Configuration options
29658  */
29659 Roo.form.FieldSet = function(config){
29660     Roo.form.FieldSet.superclass.constructor.call(this, config);
29661 };
29662
29663 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29664     /**
29665      * @cfg {String} legend
29666      * The text to display as the legend for the FieldSet (defaults to '')
29667      */
29668     /**
29669      * @cfg {String/Object} autoCreate
29670      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29671      */
29672
29673     // private
29674     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29675
29676     // private
29677     onRender : function(ct, position){
29678         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29679         if(this.legend){
29680             this.setLegend(this.legend);
29681         }
29682     },
29683
29684     // private
29685     setLegend : function(text){
29686         if(this.rendered){
29687             this.el.child('legend').update(text);
29688         }
29689     }
29690 });/*
29691  * Based on:
29692  * Ext JS Library 1.1.1
29693  * Copyright(c) 2006-2007, Ext JS, LLC.
29694  *
29695  * Originally Released Under LGPL - original licence link has changed is not relivant.
29696  *
29697  * Fork - LGPL
29698  * <script type="text/javascript">
29699  */
29700 /**
29701  * @class Roo.form.VTypes
29702  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29703  * @singleton
29704  */
29705 Roo.form.VTypes = function(){
29706     // closure these in so they are only created once.
29707     var alpha = /^[a-zA-Z_]+$/;
29708     var alphanum = /^[a-zA-Z0-9_]+$/;
29709     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29710     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29711
29712     // All these messages and functions are configurable
29713     return {
29714         /**
29715          * The function used to validate email addresses
29716          * @param {String} value The email address
29717          */
29718         'email' : function(v){
29719             return email.test(v);
29720         },
29721         /**
29722          * The error text to display when the email validation function returns false
29723          * @type String
29724          */
29725         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29726         /**
29727          * The keystroke filter mask to be applied on email input
29728          * @type RegExp
29729          */
29730         'emailMask' : /[a-z0-9_\.\-@]/i,
29731
29732         /**
29733          * The function used to validate URLs
29734          * @param {String} value The URL
29735          */
29736         'url' : function(v){
29737             return url.test(v);
29738         },
29739         /**
29740          * The error text to display when the url validation function returns false
29741          * @type String
29742          */
29743         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29744         
29745         /**
29746          * The function used to validate alpha values
29747          * @param {String} value The value
29748          */
29749         'alpha' : function(v){
29750             return alpha.test(v);
29751         },
29752         /**
29753          * The error text to display when the alpha validation function returns false
29754          * @type String
29755          */
29756         'alphaText' : 'This field should only contain letters and _',
29757         /**
29758          * The keystroke filter mask to be applied on alpha input
29759          * @type RegExp
29760          */
29761         'alphaMask' : /[a-z_]/i,
29762
29763         /**
29764          * The function used to validate alphanumeric values
29765          * @param {String} value The value
29766          */
29767         'alphanum' : function(v){
29768             return alphanum.test(v);
29769         },
29770         /**
29771          * The error text to display when the alphanumeric validation function returns false
29772          * @type String
29773          */
29774         'alphanumText' : 'This field should only contain letters, numbers and _',
29775         /**
29776          * The keystroke filter mask to be applied on alphanumeric input
29777          * @type RegExp
29778          */
29779         'alphanumMask' : /[a-z0-9_]/i
29780     };
29781 }();//<script type="text/javascript">
29782
29783 /**
29784  * @class Roo.form.FCKeditor
29785  * @extends Roo.form.TextArea
29786  * Wrapper around the FCKEditor http://www.fckeditor.net
29787  * @constructor
29788  * Creates a new FCKeditor
29789  * @param {Object} config Configuration options
29790  */
29791 Roo.form.FCKeditor = function(config){
29792     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29793     this.addEvents({
29794          /**
29795          * @event editorinit
29796          * Fired when the editor is initialized - you can add extra handlers here..
29797          * @param {FCKeditor} this
29798          * @param {Object} the FCK object.
29799          */
29800         editorinit : true
29801     });
29802     
29803     
29804 };
29805 Roo.form.FCKeditor.editors = { };
29806 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29807 {
29808     //defaultAutoCreate : {
29809     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29810     //},
29811     // private
29812     /**
29813      * @cfg {Object} fck options - see fck manual for details.
29814      */
29815     fckconfig : false,
29816     
29817     /**
29818      * @cfg {Object} fck toolbar set (Basic or Default)
29819      */
29820     toolbarSet : 'Basic',
29821     /**
29822      * @cfg {Object} fck BasePath
29823      */ 
29824     basePath : '/fckeditor/',
29825     
29826     
29827     frame : false,
29828     
29829     value : '',
29830     
29831    
29832     onRender : function(ct, position)
29833     {
29834         if(!this.el){
29835             this.defaultAutoCreate = {
29836                 tag: "textarea",
29837                 style:"width:300px;height:60px;",
29838                 autocomplete: "off"
29839             };
29840         }
29841         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29842         /*
29843         if(this.grow){
29844             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29845             if(this.preventScrollbars){
29846                 this.el.setStyle("overflow", "hidden");
29847             }
29848             this.el.setHeight(this.growMin);
29849         }
29850         */
29851         //console.log('onrender' + this.getId() );
29852         Roo.form.FCKeditor.editors[this.getId()] = this;
29853          
29854
29855         this.replaceTextarea() ;
29856         
29857     },
29858     
29859     getEditor : function() {
29860         return this.fckEditor;
29861     },
29862     /**
29863      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29864      * @param {Mixed} value The value to set
29865      */
29866     
29867     
29868     setValue : function(value)
29869     {
29870         //console.log('setValue: ' + value);
29871         
29872         if(typeof(value) == 'undefined') { // not sure why this is happending...
29873             return;
29874         }
29875         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29876         
29877         //if(!this.el || !this.getEditor()) {
29878         //    this.value = value;
29879             //this.setValue.defer(100,this,[value]);    
29880         //    return;
29881         //} 
29882         
29883         if(!this.getEditor()) {
29884             return;
29885         }
29886         
29887         this.getEditor().SetData(value);
29888         
29889         //
29890
29891     },
29892
29893     /**
29894      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29895      * @return {Mixed} value The field value
29896      */
29897     getValue : function()
29898     {
29899         
29900         if (this.frame && this.frame.dom.style.display == 'none') {
29901             return Roo.form.FCKeditor.superclass.getValue.call(this);
29902         }
29903         
29904         if(!this.el || !this.getEditor()) {
29905            
29906            // this.getValue.defer(100,this); 
29907             return this.value;
29908         }
29909        
29910         
29911         var value=this.getEditor().GetData();
29912         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29913         return Roo.form.FCKeditor.superclass.getValue.call(this);
29914         
29915
29916     },
29917
29918     /**
29919      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29920      * @return {Mixed} value The field value
29921      */
29922     getRawValue : function()
29923     {
29924         if (this.frame && this.frame.dom.style.display == 'none') {
29925             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29926         }
29927         
29928         if(!this.el || !this.getEditor()) {
29929             //this.getRawValue.defer(100,this); 
29930             return this.value;
29931             return;
29932         }
29933         
29934         
29935         
29936         var value=this.getEditor().GetData();
29937         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29938         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29939          
29940     },
29941     
29942     setSize : function(w,h) {
29943         
29944         
29945         
29946         //if (this.frame && this.frame.dom.style.display == 'none') {
29947         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29948         //    return;
29949         //}
29950         //if(!this.el || !this.getEditor()) {
29951         //    this.setSize.defer(100,this, [w,h]); 
29952         //    return;
29953         //}
29954         
29955         
29956         
29957         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29958         
29959         this.frame.dom.setAttribute('width', w);
29960         this.frame.dom.setAttribute('height', h);
29961         this.frame.setSize(w,h);
29962         
29963     },
29964     
29965     toggleSourceEdit : function(value) {
29966         
29967       
29968          
29969         this.el.dom.style.display = value ? '' : 'none';
29970         this.frame.dom.style.display = value ?  'none' : '';
29971         
29972     },
29973     
29974     
29975     focus: function(tag)
29976     {
29977         if (this.frame.dom.style.display == 'none') {
29978             return Roo.form.FCKeditor.superclass.focus.call(this);
29979         }
29980         if(!this.el || !this.getEditor()) {
29981             this.focus.defer(100,this, [tag]); 
29982             return;
29983         }
29984         
29985         
29986         
29987         
29988         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29989         this.getEditor().Focus();
29990         if (tgs.length) {
29991             if (!this.getEditor().Selection.GetSelection()) {
29992                 this.focus.defer(100,this, [tag]); 
29993                 return;
29994             }
29995             
29996             
29997             var r = this.getEditor().EditorDocument.createRange();
29998             r.setStart(tgs[0],0);
29999             r.setEnd(tgs[0],0);
30000             this.getEditor().Selection.GetSelection().removeAllRanges();
30001             this.getEditor().Selection.GetSelection().addRange(r);
30002             this.getEditor().Focus();
30003         }
30004         
30005     },
30006     
30007     
30008     
30009     replaceTextarea : function()
30010     {
30011         if ( document.getElementById( this.getId() + '___Frame' ) )
30012             return ;
30013         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30014         //{
30015             // We must check the elements firstly using the Id and then the name.
30016         var oTextarea = document.getElementById( this.getId() );
30017         
30018         var colElementsByName = document.getElementsByName( this.getId() ) ;
30019          
30020         oTextarea.style.display = 'none' ;
30021
30022         if ( oTextarea.tabIndex ) {            
30023             this.TabIndex = oTextarea.tabIndex ;
30024         }
30025         
30026         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30027         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30028         this.frame = Roo.get(this.getId() + '___Frame')
30029     },
30030     
30031     _getConfigHtml : function()
30032     {
30033         var sConfig = '' ;
30034
30035         for ( var o in this.fckconfig ) {
30036             sConfig += sConfig.length > 0  ? '&amp;' : '';
30037             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30038         }
30039
30040         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30041     },
30042     
30043     
30044     _getIFrameHtml : function()
30045     {
30046         var sFile = 'fckeditor.html' ;
30047         /* no idea what this is about..
30048         try
30049         {
30050             if ( (/fcksource=true/i).test( window.top.location.search ) )
30051                 sFile = 'fckeditor.original.html' ;
30052         }
30053         catch (e) { 
30054         */
30055
30056         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30057         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30058         
30059         
30060         var html = '<iframe id="' + this.getId() +
30061             '___Frame" src="' + sLink +
30062             '" width="' + this.width +
30063             '" height="' + this.height + '"' +
30064             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30065             ' frameborder="0" scrolling="no"></iframe>' ;
30066
30067         return html ;
30068     },
30069     
30070     _insertHtmlBefore : function( html, element )
30071     {
30072         if ( element.insertAdjacentHTML )       {
30073             // IE
30074             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30075         } else { // Gecko
30076             var oRange = document.createRange() ;
30077             oRange.setStartBefore( element ) ;
30078             var oFragment = oRange.createContextualFragment( html );
30079             element.parentNode.insertBefore( oFragment, element ) ;
30080         }
30081     }
30082     
30083     
30084   
30085     
30086     
30087     
30088     
30089
30090 });
30091
30092 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30093
30094 function FCKeditor_OnComplete(editorInstance){
30095     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30096     f.fckEditor = editorInstance;
30097     //console.log("loaded");
30098     f.fireEvent('editorinit', f, editorInstance);
30099
30100   
30101
30102  
30103
30104
30105
30106
30107
30108
30109
30110
30111
30112
30113
30114
30115
30116
30117
30118 //<script type="text/javascript">
30119 /**
30120  * @class Roo.form.GridField
30121  * @extends Roo.form.Field
30122  * Embed a grid (or editable grid into a form)
30123  * STATUS ALPHA
30124  * 
30125  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30126  * it needs 
30127  * xgrid.store = Roo.data.Store
30128  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30129  * xgrid.store.reader = Roo.data.JsonReader 
30130  * 
30131  * 
30132  * @constructor
30133  * Creates a new GridField
30134  * @param {Object} config Configuration options
30135  */
30136 Roo.form.GridField = function(config){
30137     Roo.form.GridField.superclass.constructor.call(this, config);
30138      
30139 };
30140
30141 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30142     /**
30143      * @cfg {Number} width  - used to restrict width of grid..
30144      */
30145     width : 100,
30146     /**
30147      * @cfg {Number} height - used to restrict height of grid..
30148      */
30149     height : 50,
30150      /**
30151      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30152          * 
30153          *}
30154      */
30155     xgrid : false, 
30156     /**
30157      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30158      * {tag: "input", type: "checkbox", autocomplete: "off"})
30159      */
30160    // defaultAutoCreate : { tag: 'div' },
30161     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30162     /**
30163      * @cfg {String} addTitle Text to include for adding a title.
30164      */
30165     addTitle : false,
30166     //
30167     onResize : function(){
30168         Roo.form.Field.superclass.onResize.apply(this, arguments);
30169     },
30170
30171     initEvents : function(){
30172         // Roo.form.Checkbox.superclass.initEvents.call(this);
30173         // has no events...
30174        
30175     },
30176
30177
30178     getResizeEl : function(){
30179         return this.wrap;
30180     },
30181
30182     getPositionEl : function(){
30183         return this.wrap;
30184     },
30185
30186     // private
30187     onRender : function(ct, position){
30188         
30189         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30190         var style = this.style;
30191         delete this.style;
30192         
30193         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30194         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30195         this.viewEl = this.wrap.createChild({ tag: 'div' });
30196         if (style) {
30197             this.viewEl.applyStyles(style);
30198         }
30199         if (this.width) {
30200             this.viewEl.setWidth(this.width);
30201         }
30202         if (this.height) {
30203             this.viewEl.setHeight(this.height);
30204         }
30205         //if(this.inputValue !== undefined){
30206         //this.setValue(this.value);
30207         
30208         
30209         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30210         
30211         
30212         this.grid.render();
30213         this.grid.getDataSource().on('remove', this.refreshValue, this);
30214         this.grid.getDataSource().on('update', this.refreshValue, this);
30215         this.grid.on('afteredit', this.refreshValue, this);
30216  
30217     },
30218      
30219     
30220     /**
30221      * Sets the value of the item. 
30222      * @param {String} either an object  or a string..
30223      */
30224     setValue : function(v){
30225         //this.value = v;
30226         v = v || []; // empty set..
30227         // this does not seem smart - it really only affects memoryproxy grids..
30228         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30229             var ds = this.grid.getDataSource();
30230             // assumes a json reader..
30231             var data = {}
30232             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30233             ds.loadData( data);
30234         }
30235         // clear selection so it does not get stale.
30236         if (this.grid.sm) { 
30237             this.grid.sm.clearSelections();
30238         }
30239         
30240         Roo.form.GridField.superclass.setValue.call(this, v);
30241         this.refreshValue();
30242         // should load data in the grid really....
30243     },
30244     
30245     // private
30246     refreshValue: function() {
30247          var val = [];
30248         this.grid.getDataSource().each(function(r) {
30249             val.push(r.data);
30250         });
30251         this.el.dom.value = Roo.encode(val);
30252     }
30253     
30254      
30255     
30256     
30257 });/*
30258  * Based on:
30259  * Ext JS Library 1.1.1
30260  * Copyright(c) 2006-2007, Ext JS, LLC.
30261  *
30262  * Originally Released Under LGPL - original licence link has changed is not relivant.
30263  *
30264  * Fork - LGPL
30265  * <script type="text/javascript">
30266  */
30267 /**
30268  * @class Roo.form.DisplayField
30269  * @extends Roo.form.Field
30270  * A generic Field to display non-editable data.
30271  * @constructor
30272  * Creates a new Display Field item.
30273  * @param {Object} config Configuration options
30274  */
30275 Roo.form.DisplayField = function(config){
30276     Roo.form.DisplayField.superclass.constructor.call(this, config);
30277     
30278 };
30279
30280 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30281     inputType:      'hidden',
30282     allowBlank:     true,
30283     readOnly:         true,
30284     
30285  
30286     /**
30287      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30288      */
30289     focusClass : undefined,
30290     /**
30291      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30292      */
30293     fieldClass: 'x-form-field',
30294     
30295      /**
30296      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30297      */
30298     valueRenderer: undefined,
30299     
30300     width: 100,
30301     /**
30302      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30303      * {tag: "input", type: "checkbox", autocomplete: "off"})
30304      */
30305      
30306  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30307
30308     onResize : function(){
30309         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30310         
30311     },
30312
30313     initEvents : function(){
30314         // Roo.form.Checkbox.superclass.initEvents.call(this);
30315         // has no events...
30316        
30317     },
30318
30319
30320     getResizeEl : function(){
30321         return this.wrap;
30322     },
30323
30324     getPositionEl : function(){
30325         return this.wrap;
30326     },
30327
30328     // private
30329     onRender : function(ct, position){
30330         
30331         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30332         //if(this.inputValue !== undefined){
30333         this.wrap = this.el.wrap();
30334         
30335         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30336         
30337         if (this.bodyStyle) {
30338             this.viewEl.applyStyles(this.bodyStyle);
30339         }
30340         //this.viewEl.setStyle('padding', '2px');
30341         
30342         this.setValue(this.value);
30343         
30344     },
30345 /*
30346     // private
30347     initValue : Roo.emptyFn,
30348
30349   */
30350
30351         // private
30352     onClick : function(){
30353         
30354     },
30355
30356     /**
30357      * Sets the checked state of the checkbox.
30358      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30359      */
30360     setValue : function(v){
30361         this.value = v;
30362         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30363         // this might be called before we have a dom element..
30364         if (!this.viewEl) {
30365             return;
30366         }
30367         this.viewEl.dom.innerHTML = html;
30368         Roo.form.DisplayField.superclass.setValue.call(this, v);
30369
30370     }
30371 });/*
30372  * 
30373  * Licence- LGPL
30374  * 
30375  */
30376
30377 /**
30378  * @class Roo.form.DayPicker
30379  * @extends Roo.form.Field
30380  * A Day picker show [M] [T] [W] ....
30381  * @constructor
30382  * Creates a new Day Picker
30383  * @param {Object} config Configuration options
30384  */
30385 Roo.form.DayPicker= function(config){
30386     Roo.form.DayPicker.superclass.constructor.call(this, config);
30387      
30388 };
30389
30390 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30391     /**
30392      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30393      */
30394     focusClass : undefined,
30395     /**
30396      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30397      */
30398     fieldClass: "x-form-field",
30399    
30400     /**
30401      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30402      * {tag: "input", type: "checkbox", autocomplete: "off"})
30403      */
30404     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30405     
30406    
30407     actionMode : 'viewEl', 
30408     //
30409     // private
30410  
30411     inputType : 'hidden',
30412     
30413      
30414     inputElement: false, // real input element?
30415     basedOn: false, // ????
30416     
30417     isFormField: true, // not sure where this is needed!!!!
30418
30419     onResize : function(){
30420         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30421         if(!this.boxLabel){
30422             this.el.alignTo(this.wrap, 'c-c');
30423         }
30424     },
30425
30426     initEvents : function(){
30427         Roo.form.Checkbox.superclass.initEvents.call(this);
30428         this.el.on("click", this.onClick,  this);
30429         this.el.on("change", this.onClick,  this);
30430     },
30431
30432
30433     getResizeEl : function(){
30434         return this.wrap;
30435     },
30436
30437     getPositionEl : function(){
30438         return this.wrap;
30439     },
30440
30441     
30442     // private
30443     onRender : function(ct, position){
30444         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30445        
30446         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30447         
30448         var r1 = '<table><tr>';
30449         var r2 = '<tr class="x-form-daypick-icons">';
30450         for (var i=0; i < 7; i++) {
30451             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30452             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30453         }
30454         
30455         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30456         viewEl.select('img').on('click', this.onClick, this);
30457         this.viewEl = viewEl;   
30458         
30459         
30460         // this will not work on Chrome!!!
30461         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30462         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30463         
30464         
30465           
30466
30467     },
30468
30469     // private
30470     initValue : Roo.emptyFn,
30471
30472     /**
30473      * Returns the checked state of the checkbox.
30474      * @return {Boolean} True if checked, else false
30475      */
30476     getValue : function(){
30477         return this.el.dom.value;
30478         
30479     },
30480
30481         // private
30482     onClick : function(e){ 
30483         //this.setChecked(!this.checked);
30484         Roo.get(e.target).toggleClass('x-menu-item-checked');
30485         this.refreshValue();
30486         //if(this.el.dom.checked != this.checked){
30487         //    this.setValue(this.el.dom.checked);
30488        // }
30489     },
30490     
30491     // private
30492     refreshValue : function()
30493     {
30494         var val = '';
30495         this.viewEl.select('img',true).each(function(e,i,n)  {
30496             val += e.is(".x-menu-item-checked") ? String(n) : '';
30497         });
30498         this.setValue(val, true);
30499     },
30500
30501     /**
30502      * Sets the checked state of the checkbox.
30503      * On is always based on a string comparison between inputValue and the param.
30504      * @param {Boolean/String} value - the value to set 
30505      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30506      */
30507     setValue : function(v,suppressEvent){
30508         if (!this.el.dom) {
30509             return;
30510         }
30511         var old = this.el.dom.value ;
30512         this.el.dom.value = v;
30513         if (suppressEvent) {
30514             return ;
30515         }
30516          
30517         // update display..
30518         this.viewEl.select('img',true).each(function(e,i,n)  {
30519             
30520             var on = e.is(".x-menu-item-checked");
30521             var newv = v.indexOf(String(n)) > -1;
30522             if (on != newv) {
30523                 e.toggleClass('x-menu-item-checked');
30524             }
30525             
30526         });
30527         
30528         
30529         this.fireEvent('change', this, v, old);
30530         
30531         
30532     },
30533    
30534     // handle setting of hidden value by some other method!!?!?
30535     setFromHidden: function()
30536     {
30537         if(!this.el){
30538             return;
30539         }
30540         //console.log("SET FROM HIDDEN");
30541         //alert('setFrom hidden');
30542         this.setValue(this.el.dom.value);
30543     },
30544     
30545     onDestroy : function()
30546     {
30547         if(this.viewEl){
30548             Roo.get(this.viewEl).remove();
30549         }
30550          
30551         Roo.form.DayPicker.superclass.onDestroy.call(this);
30552     }
30553
30554 });/*
30555  * RooJS Library 1.1.1
30556  * Copyright(c) 2008-2011  Alan Knowles
30557  *
30558  * License - LGPL
30559  */
30560  
30561
30562 /**
30563  * @class Roo.form.ComboCheck
30564  * @extends Roo.form.ComboBox
30565  * A combobox for multiple select items.
30566  *
30567  * FIXME - could do with a reset button..
30568  * 
30569  * @constructor
30570  * Create a new ComboCheck
30571  * @param {Object} config Configuration options
30572  */
30573 Roo.form.ComboCheck = function(config){
30574     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30575     // should verify some data...
30576     // like
30577     // hiddenName = required..
30578     // displayField = required
30579     // valudField == required
30580     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30581     var _t = this;
30582     Roo.each(req, function(e) {
30583         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30584             throw "Roo.form.ComboCheck : missing value for: " + e;
30585         }
30586     });
30587     
30588     
30589 };
30590
30591 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30592      
30593      
30594     editable : false,
30595      
30596     selectedClass: 'x-menu-item-checked', 
30597     
30598     // private
30599     onRender : function(ct, position){
30600         var _t = this;
30601         
30602         
30603         
30604         if(!this.tpl){
30605             var cls = 'x-combo-list';
30606
30607             
30608             this.tpl =  new Roo.Template({
30609                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30610                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30611                    '<span>{' + this.displayField + '}</span>' +
30612                     '</div>' 
30613                 
30614             });
30615         }
30616  
30617         
30618         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30619         this.view.singleSelect = false;
30620         this.view.multiSelect = true;
30621         this.view.toggleSelect = true;
30622         this.pageTb.add(new Roo.Toolbar.Fill(), {
30623             
30624             text: 'Done',
30625             handler: function()
30626             {
30627                 _t.collapse();
30628             }
30629         });
30630     },
30631     
30632     onViewOver : function(e, t){
30633         // do nothing...
30634         return;
30635         
30636     },
30637     
30638     onViewClick : function(doFocus,index){
30639         return;
30640         
30641     },
30642     select: function () {
30643         //Roo.log("SELECT CALLED");
30644     },
30645      
30646     selectByValue : function(xv, scrollIntoView){
30647         var ar = this.getValueArray();
30648         var sels = [];
30649         
30650         Roo.each(ar, function(v) {
30651             if(v === undefined || v === null){
30652                 return;
30653             }
30654             var r = this.findRecord(this.valueField, v);
30655             if(r){
30656                 sels.push(this.store.indexOf(r))
30657                 
30658             }
30659         },this);
30660         this.view.select(sels);
30661         return false;
30662     },
30663     
30664     
30665     
30666     onSelect : function(record, index){
30667        // Roo.log("onselect Called");
30668        // this is only called by the clear button now..
30669         this.view.clearSelections();
30670         this.setValue('[]');
30671         if (this.value != this.valueBefore) {
30672             this.fireEvent('change', this, this.value, this.valueBefore);
30673             this.valueBefore = this.value;
30674         }
30675     },
30676     getValueArray : function()
30677     {
30678         var ar = [] ;
30679         
30680         try {
30681             //Roo.log(this.value);
30682             if (typeof(this.value) == 'undefined') {
30683                 return [];
30684             }
30685             var ar = Roo.decode(this.value);
30686             return  ar instanceof Array ? ar : []; //?? valid?
30687             
30688         } catch(e) {
30689             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30690             return [];
30691         }
30692          
30693     },
30694     expand : function ()
30695     {
30696         
30697         Roo.form.ComboCheck.superclass.expand.call(this);
30698         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30699         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30700         
30701
30702     },
30703     
30704     collapse : function(){
30705         Roo.form.ComboCheck.superclass.collapse.call(this);
30706         var sl = this.view.getSelectedIndexes();
30707         var st = this.store;
30708         var nv = [];
30709         var tv = [];
30710         var r;
30711         Roo.each(sl, function(i) {
30712             r = st.getAt(i);
30713             nv.push(r.get(this.valueField));
30714         },this);
30715         this.setValue(Roo.encode(nv));
30716         if (this.value != this.valueBefore) {
30717
30718             this.fireEvent('change', this, this.value, this.valueBefore);
30719             this.valueBefore = this.value;
30720         }
30721         
30722     },
30723     
30724     setValue : function(v){
30725         // Roo.log(v);
30726         this.value = v;
30727         
30728         var vals = this.getValueArray();
30729         var tv = [];
30730         Roo.each(vals, function(k) {
30731             var r = this.findRecord(this.valueField, k);
30732             if(r){
30733                 tv.push(r.data[this.displayField]);
30734             }else if(this.valueNotFoundText !== undefined){
30735                 tv.push( this.valueNotFoundText );
30736             }
30737         },this);
30738        // Roo.log(tv);
30739         
30740         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30741         this.hiddenField.value = v;
30742         this.value = v;
30743     }
30744     
30745 });/*
30746  * Based on:
30747  * Ext JS Library 1.1.1
30748  * Copyright(c) 2006-2007, Ext JS, LLC.
30749  *
30750  * Originally Released Under LGPL - original licence link has changed is not relivant.
30751  *
30752  * Fork - LGPL
30753  * <script type="text/javascript">
30754  */
30755  
30756 /**
30757  * @class Roo.form.Signature
30758  * @extends Roo.form.Field
30759  * Signature field.  
30760  * @constructor
30761  * 
30762  * @param {Object} config Configuration options
30763  */
30764
30765 Roo.form.Signature = function(config){
30766     Roo.form.Signature.superclass.constructor.call(this, config);
30767     
30768     this.addEvents({// not in used??
30769          /**
30770          * @event confirm
30771          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30772              * @param {Roo.form.Signature} combo This combo box
30773              */
30774         'confirm' : true,
30775         /**
30776          * @event reset
30777          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30778              * @param {Roo.form.ComboBox} combo This combo box
30779              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30780              */
30781         'reset' : true
30782     });
30783 };
30784
30785 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30786     /**
30787      * @cfg {Object} labels Label to use when rendering a form.
30788      * defaults to 
30789      * labels : { 
30790      *      clear : "Clear",
30791      *      confirm : "Confirm"
30792      *  }
30793      */
30794     labels : { 
30795         clear : "Clear",
30796         confirm : "Confirm"
30797     },
30798     /**
30799      * @cfg {Number} width The signature panel width (defaults to 300)
30800      */
30801     width: 300,
30802     /**
30803      * @cfg {Number} height The signature panel height (defaults to 100)
30804      */
30805     height : 100,
30806     /**
30807      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30808      */
30809     allowBlank : false,
30810     
30811     //private
30812     // {Object} signPanel The signature SVG panel element (defaults to {})
30813     signPanel : {},
30814     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30815     isMouseDown : false,
30816     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30817     isConfirmed : false,
30818     // {String} signatureTmp SVG mapping string (defaults to empty string)
30819     signatureTmp : '',
30820     
30821     
30822     defaultAutoCreate : { // modified by initCompnoent..
30823         tag: "input",
30824         type:"hidden"
30825     },
30826
30827     // private
30828     onRender : function(ct, position){
30829         
30830         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30831         
30832         this.wrap = this.el.wrap({
30833             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30834         });
30835         
30836         this.createToolbar(this);
30837         this.signPanel = this.wrap.createChild({
30838                 tag: 'div',
30839                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30840             }, this.el
30841         );
30842             
30843         this.svgID = Roo.id();
30844         this.svgEl = this.signPanel.createChild({
30845               xmlns : 'http://www.w3.org/2000/svg',
30846               tag : 'svg',
30847               id : this.svgID + "-svg",
30848               width: this.width,
30849               height: this.height,
30850               viewBox: '0 0 '+this.width+' '+this.height,
30851               cn : [
30852                 {
30853                     tag: "rect",
30854                     id: this.svgID + "-svg-r",
30855                     width: this.width,
30856                     height: this.height,
30857                     fill: "#ffa"
30858                 },
30859                 {
30860                     tag: "line",
30861                     id: this.svgID + "-svg-l",
30862                     x1: "0", // start
30863                     y1: (this.height*0.8), // start set the line in 80% of height
30864                     x2: this.width, // end
30865                     y2: (this.height*0.8), // end set the line in 80% of height
30866                     'stroke': "#666",
30867                     'stroke-width': "1",
30868                     'stroke-dasharray': "3",
30869                     'shape-rendering': "crispEdges",
30870                     'pointer-events': "none"
30871                 },
30872                 {
30873                     tag: "path",
30874                     id: this.svgID + "-svg-p",
30875                     'stroke': "navy",
30876                     'stroke-width': "3",
30877                     'fill': "none",
30878                     'pointer-events': 'none'
30879                 }
30880               ]
30881         });
30882         this.createSVG();
30883         this.svgBox = this.svgEl.dom.getScreenCTM();
30884     },
30885     createSVG : function(){ 
30886         var svg = this.signPanel;
30887         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30888         var t = this;
30889
30890         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30891         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30892         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30893         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30894         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30895         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30896         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30897         
30898     },
30899     isTouchEvent : function(e){
30900         return e.type.match(/^touch/);
30901     },
30902     getCoords : function (e) {
30903         var pt    = this.svgEl.dom.createSVGPoint();
30904         pt.x = e.clientX; 
30905         pt.y = e.clientY;
30906         if (this.isTouchEvent(e)) {
30907             pt.x =  e.targetTouches[0].clientX 
30908             pt.y = e.targetTouches[0].clientY;
30909         }
30910         var a = this.svgEl.dom.getScreenCTM();
30911         var b = a.inverse();
30912         var mx = pt.matrixTransform(b);
30913         return mx.x + ',' + mx.y;
30914     },
30915     //mouse event headler 
30916     down : function (e) {
30917         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30918         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30919         
30920         this.isMouseDown = true;
30921         
30922         e.preventDefault();
30923     },
30924     move : function (e) {
30925         if (this.isMouseDown) {
30926             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30927             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30928         }
30929         
30930         e.preventDefault();
30931     },
30932     up : function (e) {
30933         this.isMouseDown = false;
30934         var sp = this.signatureTmp.split(' ');
30935         
30936         if(sp.length > 1){
30937             if(!sp[sp.length-2].match(/^L/)){
30938                 sp.pop();
30939                 sp.pop();
30940                 sp.push("");
30941                 this.signatureTmp = sp.join(" ");
30942             }
30943         }
30944         if(this.getValue() != this.signatureTmp){
30945             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30946             this.isConfirmed = false;
30947         }
30948         e.preventDefault();
30949     },
30950     
30951     /**
30952      * Protected method that will not generally be called directly. It
30953      * is called when the editor creates its toolbar. Override this method if you need to
30954      * add custom toolbar buttons.
30955      * @param {HtmlEditor} editor
30956      */
30957     createToolbar : function(editor){
30958          function btn(id, toggle, handler){
30959             var xid = fid + '-'+ id ;
30960             return {
30961                 id : xid,
30962                 cmd : id,
30963                 cls : 'x-btn-icon x-edit-'+id,
30964                 enableToggle:toggle !== false,
30965                 scope: editor, // was editor...
30966                 handler:handler||editor.relayBtnCmd,
30967                 clickEvent:'mousedown',
30968                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
30969                 tabIndex:-1
30970             };
30971         }
30972         
30973         
30974         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
30975         this.tb = tb;
30976         this.tb.add(
30977            {
30978                 cls : ' x-signature-btn x-signature-'+id,
30979                 scope: editor, // was editor...
30980                 handler: this.reset,
30981                 clickEvent:'mousedown',
30982                 text: this.labels.clear
30983             },
30984             {
30985                  xtype : 'Fill',
30986                  xns: Roo.Toolbar
30987             }, 
30988             {
30989                 cls : '  x-signature-btn x-signature-'+id,
30990                 scope: editor, // was editor...
30991                 handler: this.confirmHandler,
30992                 clickEvent:'mousedown',
30993                 text: this.labels.confirm
30994             }
30995         );
30996     
30997     },
30998     //public
30999     /**
31000      * when user is clicked confirm then show this image.....
31001      * 
31002      * @return {String} Image Data URI
31003      */
31004     getImageDataURI : function(){
31005         var svg = this.svgEl.dom.parentNode.innerHTML;
31006         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31007         return src; 
31008     },
31009     /**
31010      * 
31011      * @return {Boolean} this.isConfirmed
31012      */
31013     getConfirmed : function(){
31014         return this.isConfirmed;
31015     },
31016     /**
31017      * 
31018      * @return {Number} this.width
31019      */
31020     getWidth : function(){
31021         return this.width;
31022     },
31023     /**
31024      * 
31025      * @return {Number} this.height
31026      */
31027     getHeight : function(){
31028         return this.height;
31029     },
31030     // private
31031     getSignature : function(){
31032         return this.signatureTmp;
31033     },
31034     // private
31035     reset : function(){
31036         this.signatureTmp = '';
31037         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31038         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31039         this.isConfirmed = false;
31040         Roo.form.Signature.superclass.reset.call(this);
31041     },
31042     setSignature : function(s){
31043         this.signatureTmp = s;
31044         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31045         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31046         this.setValue(s);
31047         this.isConfirmed = false;
31048         Roo.form.Signature.superclass.reset.call(this);
31049     }, 
31050     test : function(){
31051 //        Roo.log(this.signPanel.dom.contentWindow.up())
31052     },
31053     //private
31054     setConfirmed : function(){
31055         
31056         
31057         
31058 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31059     },
31060     // private
31061     confirmHandler : function(){
31062         if(!this.getSignature()){
31063             return;
31064         }
31065         
31066         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31067         this.setValue(this.getSignature());
31068         this.isConfirmed = true;
31069         
31070         this.fireEvent('confirm', this);
31071     },
31072     // private
31073     // Subclasses should provide the validation implementation by overriding this
31074     validateValue : function(value){
31075         if(this.allowBlank){
31076             return true;
31077         }
31078         
31079         if(this.isConfirmed){
31080             return true;
31081         }
31082         return false;
31083     }
31084 });/*
31085  * Based on:
31086  * Ext JS Library 1.1.1
31087  * Copyright(c) 2006-2007, Ext JS, LLC.
31088  *
31089  * Originally Released Under LGPL - original licence link has changed is not relivant.
31090  *
31091  * Fork - LGPL
31092  * <script type="text/javascript">
31093  */
31094  
31095
31096 /**
31097  * @class Roo.form.ComboBox
31098  * @extends Roo.form.TriggerField
31099  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31100  * @constructor
31101  * Create a new ComboBox.
31102  * @param {Object} config Configuration options
31103  */
31104 Roo.form.Select = function(config){
31105     Roo.form.Select.superclass.constructor.call(this, config);
31106      
31107 };
31108
31109 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31110     /**
31111      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31112      */
31113     /**
31114      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31115      * rendering into an Roo.Editor, defaults to false)
31116      */
31117     /**
31118      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31119      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31120      */
31121     /**
31122      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31123      */
31124     /**
31125      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31126      * the dropdown list (defaults to undefined, with no header element)
31127      */
31128
31129      /**
31130      * @cfg {String/Roo.Template} tpl The template to use to render the output
31131      */
31132      
31133     // private
31134     defaultAutoCreate : {tag: "select"  },
31135     /**
31136      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31137      */
31138     listWidth: undefined,
31139     /**
31140      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31141      * mode = 'remote' or 'text' if mode = 'local')
31142      */
31143     displayField: undefined,
31144     /**
31145      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31146      * mode = 'remote' or 'value' if mode = 'local'). 
31147      * Note: use of a valueField requires the user make a selection
31148      * in order for a value to be mapped.
31149      */
31150     valueField: undefined,
31151     
31152     
31153     /**
31154      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31155      * field's data value (defaults to the underlying DOM element's name)
31156      */
31157     hiddenName: undefined,
31158     /**
31159      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31160      */
31161     listClass: '',
31162     /**
31163      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31164      */
31165     selectedClass: 'x-combo-selected',
31166     /**
31167      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31168      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31169      * which displays a downward arrow icon).
31170      */
31171     triggerClass : 'x-form-arrow-trigger',
31172     /**
31173      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31174      */
31175     shadow:'sides',
31176     /**
31177      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31178      * anchor positions (defaults to 'tl-bl')
31179      */
31180     listAlign: 'tl-bl?',
31181     /**
31182      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31183      */
31184     maxHeight: 300,
31185     /**
31186      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31187      * query specified by the allQuery config option (defaults to 'query')
31188      */
31189     triggerAction: 'query',
31190     /**
31191      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31192      * (defaults to 4, does not apply if editable = false)
31193      */
31194     minChars : 4,
31195     /**
31196      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31197      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31198      */
31199     typeAhead: false,
31200     /**
31201      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31202      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31203      */
31204     queryDelay: 500,
31205     /**
31206      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31207      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31208      */
31209     pageSize: 0,
31210     /**
31211      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31212      * when editable = true (defaults to false)
31213      */
31214     selectOnFocus:false,
31215     /**
31216      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31217      */
31218     queryParam: 'query',
31219     /**
31220      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31221      * when mode = 'remote' (defaults to 'Loading...')
31222      */
31223     loadingText: 'Loading...',
31224     /**
31225      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31226      */
31227     resizable: false,
31228     /**
31229      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31230      */
31231     handleHeight : 8,
31232     /**
31233      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31234      * traditional select (defaults to true)
31235      */
31236     editable: true,
31237     /**
31238      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31239      */
31240     allQuery: '',
31241     /**
31242      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31243      */
31244     mode: 'remote',
31245     /**
31246      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31247      * listWidth has a higher value)
31248      */
31249     minListWidth : 70,
31250     /**
31251      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31252      * allow the user to set arbitrary text into the field (defaults to false)
31253      */
31254     forceSelection:false,
31255     /**
31256      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31257      * if typeAhead = true (defaults to 250)
31258      */
31259     typeAheadDelay : 250,
31260     /**
31261      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31262      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31263      */
31264     valueNotFoundText : undefined,
31265     
31266     /**
31267      * @cfg {String} defaultValue The value displayed after loading the store.
31268      */
31269     defaultValue: '',
31270     
31271     /**
31272      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31273      */
31274     blockFocus : false,
31275     
31276     /**
31277      * @cfg {Boolean} disableClear Disable showing of clear button.
31278      */
31279     disableClear : false,
31280     /**
31281      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31282      */
31283     alwaysQuery : false,
31284     
31285     //private
31286     addicon : false,
31287     editicon: false,
31288     
31289     // element that contains real text value.. (when hidden is used..)
31290      
31291     // private
31292     onRender : function(ct, position){
31293         Roo.form.Field.prototype.onRender.call(this, ct, position);
31294         
31295         if(this.store){
31296             this.store.on('beforeload', this.onBeforeLoad, this);
31297             this.store.on('load', this.onLoad, this);
31298             this.store.on('loadexception', this.onLoadException, this);
31299             this.store.load({});
31300         }
31301         
31302         
31303         
31304     },
31305
31306     // private
31307     initEvents : function(){
31308         //Roo.form.ComboBox.superclass.initEvents.call(this);
31309  
31310     },
31311
31312     onDestroy : function(){
31313        
31314         if(this.store){
31315             this.store.un('beforeload', this.onBeforeLoad, this);
31316             this.store.un('load', this.onLoad, this);
31317             this.store.un('loadexception', this.onLoadException, this);
31318         }
31319         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31320     },
31321
31322     // private
31323     fireKey : function(e){
31324         if(e.isNavKeyPress() && !this.list.isVisible()){
31325             this.fireEvent("specialkey", this, e);
31326         }
31327     },
31328
31329     // private
31330     onResize: function(w, h){
31331         
31332         return; 
31333     
31334         
31335     },
31336
31337     /**
31338      * Allow or prevent the user from directly editing the field text.  If false is passed,
31339      * the user will only be able to select from the items defined in the dropdown list.  This method
31340      * is the runtime equivalent of setting the 'editable' config option at config time.
31341      * @param {Boolean} value True to allow the user to directly edit the field text
31342      */
31343     setEditable : function(value){
31344          
31345     },
31346
31347     // private
31348     onBeforeLoad : function(){
31349         
31350         Roo.log("Select before load");
31351         return;
31352     
31353         this.innerList.update(this.loadingText ?
31354                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31355         //this.restrictHeight();
31356         this.selectedIndex = -1;
31357     },
31358
31359     // private
31360     onLoad : function(){
31361
31362     
31363         var dom = this.el.dom;
31364         dom.innerHTML = '';
31365          var od = dom.ownerDocument;
31366          
31367         if (this.emptyText) {
31368             var op = od.createElement('option');
31369             op.setAttribute('value', '');
31370             op.innerHTML = String.format('{0}', this.emptyText);
31371             dom.appendChild(op);
31372         }
31373         if(this.store.getCount() > 0){
31374            
31375             var vf = this.valueField;
31376             var df = this.displayField;
31377             this.store.data.each(function(r) {
31378                 // which colmsn to use... testing - cdoe / title..
31379                 var op = od.createElement('option');
31380                 op.setAttribute('value', r.data[vf]);
31381                 op.innerHTML = String.format('{0}', r.data[df]);
31382                 dom.appendChild(op);
31383             });
31384             if (typeof(this.defaultValue != 'undefined')) {
31385                 this.setValue(this.defaultValue);
31386             }
31387             
31388              
31389         }else{
31390             //this.onEmptyResults();
31391         }
31392         //this.el.focus();
31393     },
31394     // private
31395     onLoadException : function()
31396     {
31397         dom.innerHTML = '';
31398             
31399         Roo.log("Select on load exception");
31400         return;
31401     
31402         this.collapse();
31403         Roo.log(this.store.reader.jsonData);
31404         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31405             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31406         }
31407         
31408         
31409     },
31410     // private
31411     onTypeAhead : function(){
31412          
31413     },
31414
31415     // private
31416     onSelect : function(record, index){
31417         Roo.log('on select?');
31418         return;
31419         if(this.fireEvent('beforeselect', this, record, index) !== false){
31420             this.setFromData(index > -1 ? record.data : false);
31421             this.collapse();
31422             this.fireEvent('select', this, record, index);
31423         }
31424     },
31425
31426     /**
31427      * Returns the currently selected field value or empty string if no value is set.
31428      * @return {String} value The selected value
31429      */
31430     getValue : function(){
31431         var dom = this.el.dom;
31432         this.value = dom.options[dom.selectedIndex].value;
31433         return this.value;
31434         
31435     },
31436
31437     /**
31438      * Clears any text/value currently set in the field
31439      */
31440     clearValue : function(){
31441         this.value = '';
31442         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31443         
31444     },
31445
31446     /**
31447      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31448      * will be displayed in the field.  If the value does not match the data value of an existing item,
31449      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31450      * Otherwise the field will be blank (although the value will still be set).
31451      * @param {String} value The value to match
31452      */
31453     setValue : function(v){
31454         var d = this.el.dom;
31455         for (var i =0; i < d.options.length;i++) {
31456             if (v == d.options[i].value) {
31457                 d.selectedIndex = i;
31458                 this.value = v;
31459                 return;
31460             }
31461         }
31462         this.clearValue();
31463     },
31464     /**
31465      * @property {Object} the last set data for the element
31466      */
31467     
31468     lastData : false,
31469     /**
31470      * Sets the value of the field based on a object which is related to the record format for the store.
31471      * @param {Object} value the value to set as. or false on reset?
31472      */
31473     setFromData : function(o){
31474         Roo.log('setfrom data?');
31475          
31476         
31477         
31478     },
31479     // private
31480     reset : function(){
31481         this.clearValue();
31482     },
31483     // private
31484     findRecord : function(prop, value){
31485         
31486         return false;
31487     
31488         var record;
31489         if(this.store.getCount() > 0){
31490             this.store.each(function(r){
31491                 if(r.data[prop] == value){
31492                     record = r;
31493                     return false;
31494                 }
31495                 return true;
31496             });
31497         }
31498         return record;
31499     },
31500     
31501     getName: function()
31502     {
31503         // returns hidden if it's set..
31504         if (!this.rendered) {return ''};
31505         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31506         
31507     },
31508      
31509
31510     
31511
31512     // private
31513     onEmptyResults : function(){
31514         Roo.log('empty results');
31515         //this.collapse();
31516     },
31517
31518     /**
31519      * Returns true if the dropdown list is expanded, else false.
31520      */
31521     isExpanded : function(){
31522         return false;
31523     },
31524
31525     /**
31526      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31527      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31528      * @param {String} value The data value of the item to select
31529      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31530      * selected item if it is not currently in view (defaults to true)
31531      * @return {Boolean} True if the value matched an item in the list, else false
31532      */
31533     selectByValue : function(v, scrollIntoView){
31534         Roo.log('select By Value');
31535         return false;
31536     
31537         if(v !== undefined && v !== null){
31538             var r = this.findRecord(this.valueField || this.displayField, v);
31539             if(r){
31540                 this.select(this.store.indexOf(r), scrollIntoView);
31541                 return true;
31542             }
31543         }
31544         return false;
31545     },
31546
31547     /**
31548      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31549      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31550      * @param {Number} index The zero-based index of the list item to select
31551      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31552      * selected item if it is not currently in view (defaults to true)
31553      */
31554     select : function(index, scrollIntoView){
31555         Roo.log('select ');
31556         return  ;
31557         
31558         this.selectedIndex = index;
31559         this.view.select(index);
31560         if(scrollIntoView !== false){
31561             var el = this.view.getNode(index);
31562             if(el){
31563                 this.innerList.scrollChildIntoView(el, false);
31564             }
31565         }
31566     },
31567
31568       
31569
31570     // private
31571     validateBlur : function(){
31572         
31573         return;
31574         
31575     },
31576
31577     // private
31578     initQuery : function(){
31579         this.doQuery(this.getRawValue());
31580     },
31581
31582     // private
31583     doForce : function(){
31584         if(this.el.dom.value.length > 0){
31585             this.el.dom.value =
31586                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31587              
31588         }
31589     },
31590
31591     /**
31592      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31593      * query allowing the query action to be canceled if needed.
31594      * @param {String} query The SQL query to execute
31595      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31596      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31597      * saved in the current store (defaults to false)
31598      */
31599     doQuery : function(q, forceAll){
31600         
31601         Roo.log('doQuery?');
31602         if(q === undefined || q === null){
31603             q = '';
31604         }
31605         var qe = {
31606             query: q,
31607             forceAll: forceAll,
31608             combo: this,
31609             cancel:false
31610         };
31611         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31612             return false;
31613         }
31614         q = qe.query;
31615         forceAll = qe.forceAll;
31616         if(forceAll === true || (q.length >= this.minChars)){
31617             if(this.lastQuery != q || this.alwaysQuery){
31618                 this.lastQuery = q;
31619                 if(this.mode == 'local'){
31620                     this.selectedIndex = -1;
31621                     if(forceAll){
31622                         this.store.clearFilter();
31623                     }else{
31624                         this.store.filter(this.displayField, q);
31625                     }
31626                     this.onLoad();
31627                 }else{
31628                     this.store.baseParams[this.queryParam] = q;
31629                     this.store.load({
31630                         params: this.getParams(q)
31631                     });
31632                     this.expand();
31633                 }
31634             }else{
31635                 this.selectedIndex = -1;
31636                 this.onLoad();   
31637             }
31638         }
31639     },
31640
31641     // private
31642     getParams : function(q){
31643         var p = {};
31644         //p[this.queryParam] = q;
31645         if(this.pageSize){
31646             p.start = 0;
31647             p.limit = this.pageSize;
31648         }
31649         return p;
31650     },
31651
31652     /**
31653      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31654      */
31655     collapse : function(){
31656         
31657     },
31658
31659     // private
31660     collapseIf : function(e){
31661         
31662     },
31663
31664     /**
31665      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31666      */
31667     expand : function(){
31668         
31669     } ,
31670
31671     // private
31672      
31673
31674     /** 
31675     * @cfg {Boolean} grow 
31676     * @hide 
31677     */
31678     /** 
31679     * @cfg {Number} growMin 
31680     * @hide 
31681     */
31682     /** 
31683     * @cfg {Number} growMax 
31684     * @hide 
31685     */
31686     /**
31687      * @hide
31688      * @method autoSize
31689      */
31690     
31691     setWidth : function()
31692     {
31693         
31694     },
31695     getResizeEl : function(){
31696         return this.el;
31697     }
31698 });//<script type="text/javasscript">
31699  
31700
31701 /**
31702  * @class Roo.DDView
31703  * A DnD enabled version of Roo.View.
31704  * @param {Element/String} container The Element in which to create the View.
31705  * @param {String} tpl The template string used to create the markup for each element of the View
31706  * @param {Object} config The configuration properties. These include all the config options of
31707  * {@link Roo.View} plus some specific to this class.<br>
31708  * <p>
31709  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31710  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31711  * <p>
31712  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31713 .x-view-drag-insert-above {
31714         border-top:1px dotted #3366cc;
31715 }
31716 .x-view-drag-insert-below {
31717         border-bottom:1px dotted #3366cc;
31718 }
31719 </code></pre>
31720  * 
31721  */
31722  
31723 Roo.DDView = function(container, tpl, config) {
31724     Roo.DDView.superclass.constructor.apply(this, arguments);
31725     this.getEl().setStyle("outline", "0px none");
31726     this.getEl().unselectable();
31727     if (this.dragGroup) {
31728                 this.setDraggable(this.dragGroup.split(","));
31729     }
31730     if (this.dropGroup) {
31731                 this.setDroppable(this.dropGroup.split(","));
31732     }
31733     if (this.deletable) {
31734         this.setDeletable();
31735     }
31736     this.isDirtyFlag = false;
31737         this.addEvents({
31738                 "drop" : true
31739         });
31740 };
31741
31742 Roo.extend(Roo.DDView, Roo.View, {
31743 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31744 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31745 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31746 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31747
31748         isFormField: true,
31749
31750         reset: Roo.emptyFn,
31751         
31752         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31753
31754         validate: function() {
31755                 return true;
31756         },
31757         
31758         destroy: function() {
31759                 this.purgeListeners();
31760                 this.getEl.removeAllListeners();
31761                 this.getEl().remove();
31762                 if (this.dragZone) {
31763                         if (this.dragZone.destroy) {
31764                                 this.dragZone.destroy();
31765                         }
31766                 }
31767                 if (this.dropZone) {
31768                         if (this.dropZone.destroy) {
31769                                 this.dropZone.destroy();
31770                         }
31771                 }
31772         },
31773
31774 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31775         getName: function() {
31776                 return this.name;
31777         },
31778
31779 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31780         setValue: function(v) {
31781                 if (!this.store) {
31782                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31783                 }
31784                 var data = {};
31785                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31786                 this.store.proxy = new Roo.data.MemoryProxy(data);
31787                 this.store.load();
31788         },
31789
31790 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31791         getValue: function() {
31792                 var result = '(';
31793                 this.store.each(function(rec) {
31794                         result += rec.id + ',';
31795                 });
31796                 return result.substr(0, result.length - 1) + ')';
31797         },
31798         
31799         getIds: function() {
31800                 var i = 0, result = new Array(this.store.getCount());
31801                 this.store.each(function(rec) {
31802                         result[i++] = rec.id;
31803                 });
31804                 return result;
31805         },
31806         
31807         isDirty: function() {
31808                 return this.isDirtyFlag;
31809         },
31810
31811 /**
31812  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31813  *      whole Element becomes the target, and this causes the drop gesture to append.
31814  */
31815     getTargetFromEvent : function(e) {
31816                 var target = e.getTarget();
31817                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31818                 target = target.parentNode;
31819                 }
31820                 if (!target) {
31821                         target = this.el.dom.lastChild || this.el.dom;
31822                 }
31823                 return target;
31824     },
31825
31826 /**
31827  *      Create the drag data which consists of an object which has the property "ddel" as
31828  *      the drag proxy element. 
31829  */
31830     getDragData : function(e) {
31831         var target = this.findItemFromChild(e.getTarget());
31832                 if(target) {
31833                         this.handleSelection(e);
31834                         var selNodes = this.getSelectedNodes();
31835             var dragData = {
31836                 source: this,
31837                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31838                 nodes: selNodes,
31839                 records: []
31840                         };
31841                         var selectedIndices = this.getSelectedIndexes();
31842                         for (var i = 0; i < selectedIndices.length; i++) {
31843                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31844                         }
31845                         if (selNodes.length == 1) {
31846                                 dragData.ddel = target.cloneNode(true); // the div element
31847                         } else {
31848                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31849                                 div.className = 'multi-proxy';
31850                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31851                                         div.appendChild(selNodes[i].cloneNode(true));
31852                                 }
31853                                 dragData.ddel = div;
31854                         }
31855             //console.log(dragData)
31856             //console.log(dragData.ddel.innerHTML)
31857                         return dragData;
31858                 }
31859         //console.log('nodragData')
31860                 return false;
31861     },
31862     
31863 /**     Specify to which ddGroup items in this DDView may be dragged. */
31864     setDraggable: function(ddGroup) {
31865         if (ddGroup instanceof Array) {
31866                 Roo.each(ddGroup, this.setDraggable, this);
31867                 return;
31868         }
31869         if (this.dragZone) {
31870                 this.dragZone.addToGroup(ddGroup);
31871         } else {
31872                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31873                                 containerScroll: true,
31874                                 ddGroup: ddGroup 
31875
31876                         });
31877 //                      Draggability implies selection. DragZone's mousedown selects the element.
31878                         if (!this.multiSelect) { this.singleSelect = true; }
31879
31880 //                      Wire the DragZone's handlers up to methods in *this*
31881                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31882                 }
31883     },
31884
31885 /**     Specify from which ddGroup this DDView accepts drops. */
31886     setDroppable: function(ddGroup) {
31887         if (ddGroup instanceof Array) {
31888                 Roo.each(ddGroup, this.setDroppable, this);
31889                 return;
31890         }
31891         if (this.dropZone) {
31892                 this.dropZone.addToGroup(ddGroup);
31893         } else {
31894                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31895                                 containerScroll: true,
31896                                 ddGroup: ddGroup
31897                         });
31898
31899 //                      Wire the DropZone's handlers up to methods in *this*
31900                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31901                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31902                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31903                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31904                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31905                 }
31906     },
31907
31908 /**     Decide whether to drop above or below a View node. */
31909     getDropPoint : function(e, n, dd){
31910         if (n == this.el.dom) { return "above"; }
31911                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31912                 var c = t + (b - t) / 2;
31913                 var y = Roo.lib.Event.getPageY(e);
31914                 if(y <= c) {
31915                         return "above";
31916                 }else{
31917                         return "below";
31918                 }
31919     },
31920
31921     onNodeEnter : function(n, dd, e, data){
31922                 return false;
31923     },
31924     
31925     onNodeOver : function(n, dd, e, data){
31926                 var pt = this.getDropPoint(e, n, dd);
31927                 // set the insert point style on the target node
31928                 var dragElClass = this.dropNotAllowed;
31929                 if (pt) {
31930                         var targetElClass;
31931                         if (pt == "above"){
31932                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31933                                 targetElClass = "x-view-drag-insert-above";
31934                         } else {
31935                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31936                                 targetElClass = "x-view-drag-insert-below";
31937                         }
31938                         if (this.lastInsertClass != targetElClass){
31939                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31940                                 this.lastInsertClass = targetElClass;
31941                         }
31942                 }
31943                 return dragElClass;
31944         },
31945
31946     onNodeOut : function(n, dd, e, data){
31947                 this.removeDropIndicators(n);
31948     },
31949
31950     onNodeDrop : function(n, dd, e, data){
31951         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31952                 return false;
31953         }
31954         var pt = this.getDropPoint(e, n, dd);
31955                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
31956                 if (pt == "below") { insertAt++; }
31957                 for (var i = 0; i < data.records.length; i++) {
31958                         var r = data.records[i];
31959                         var dup = this.store.getById(r.id);
31960                         if (dup && (dd != this.dragZone)) {
31961                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
31962                         } else {
31963                                 if (data.copy) {
31964                                         this.store.insert(insertAt++, r.copy());
31965                                 } else {
31966                                         data.source.isDirtyFlag = true;
31967                                         r.store.remove(r);
31968                                         this.store.insert(insertAt++, r);
31969                                 }
31970                                 this.isDirtyFlag = true;
31971                         }
31972                 }
31973                 this.dragZone.cachedTarget = null;
31974                 return true;
31975     },
31976
31977     removeDropIndicators : function(n){
31978                 if(n){
31979                         Roo.fly(n).removeClass([
31980                                 "x-view-drag-insert-above",
31981                                 "x-view-drag-insert-below"]);
31982                         this.lastInsertClass = "_noclass";
31983                 }
31984     },
31985
31986 /**
31987  *      Utility method. Add a delete option to the DDView's context menu.
31988  *      @param {String} imageUrl The URL of the "delete" icon image.
31989  */
31990         setDeletable: function(imageUrl) {
31991                 if (!this.singleSelect && !this.multiSelect) {
31992                         this.singleSelect = true;
31993                 }
31994                 var c = this.getContextMenu();
31995                 this.contextMenu.on("itemclick", function(item) {
31996                         switch (item.id) {
31997                                 case "delete":
31998                                         this.remove(this.getSelectedIndexes());
31999                                         break;
32000                         }
32001                 }, this);
32002                 this.contextMenu.add({
32003                         icon: imageUrl,
32004                         id: "delete",
32005                         text: 'Delete'
32006                 });
32007         },
32008         
32009 /**     Return the context menu for this DDView. */
32010         getContextMenu: function() {
32011                 if (!this.contextMenu) {
32012 //                      Create the View's context menu
32013                         this.contextMenu = new Roo.menu.Menu({
32014                                 id: this.id + "-contextmenu"
32015                         });
32016                         this.el.on("contextmenu", this.showContextMenu, this);
32017                 }
32018                 return this.contextMenu;
32019         },
32020         
32021         disableContextMenu: function() {
32022                 if (this.contextMenu) {
32023                         this.el.un("contextmenu", this.showContextMenu, this);
32024                 }
32025         },
32026
32027         showContextMenu: function(e, item) {
32028         item = this.findItemFromChild(e.getTarget());
32029                 if (item) {
32030                         e.stopEvent();
32031                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32032                         this.contextMenu.showAt(e.getXY());
32033             }
32034     },
32035
32036 /**
32037  *      Remove {@link Roo.data.Record}s at the specified indices.
32038  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32039  */
32040     remove: function(selectedIndices) {
32041                 selectedIndices = [].concat(selectedIndices);
32042                 for (var i = 0; i < selectedIndices.length; i++) {
32043                         var rec = this.store.getAt(selectedIndices[i]);
32044                         this.store.remove(rec);
32045                 }
32046     },
32047
32048 /**
32049  *      Double click fires the event, but also, if this is draggable, and there is only one other
32050  *      related DropZone, it transfers the selected node.
32051  */
32052     onDblClick : function(e){
32053         var item = this.findItemFromChild(e.getTarget());
32054         if(item){
32055             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32056                 return false;
32057             }
32058             if (this.dragGroup) {
32059                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32060                     while (targets.indexOf(this.dropZone) > -1) {
32061                             targets.remove(this.dropZone);
32062                                 }
32063                     if (targets.length == 1) {
32064                                         this.dragZone.cachedTarget = null;
32065                         var el = Roo.get(targets[0].getEl());
32066                         var box = el.getBox(true);
32067                         targets[0].onNodeDrop(el.dom, {
32068                                 target: el.dom,
32069                                 xy: [box.x, box.y + box.height - 1]
32070                         }, null, this.getDragData(e));
32071                     }
32072                 }
32073         }
32074     },
32075     
32076     handleSelection: function(e) {
32077                 this.dragZone.cachedTarget = null;
32078         var item = this.findItemFromChild(e.getTarget());
32079         if (!item) {
32080                 this.clearSelections(true);
32081                 return;
32082         }
32083                 if (item && (this.multiSelect || this.singleSelect)){
32084                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32085                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32086                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32087                                 this.unselect(item);
32088                         } else {
32089                                 this.select(item, this.multiSelect && e.ctrlKey);
32090                                 this.lastSelection = item;
32091                         }
32092                 }
32093     },
32094
32095     onItemClick : function(item, index, e){
32096                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32097                         return false;
32098                 }
32099                 return true;
32100     },
32101
32102     unselect : function(nodeInfo, suppressEvent){
32103                 var node = this.getNode(nodeInfo);
32104                 if(node && this.isSelected(node)){
32105                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32106                                 Roo.fly(node).removeClass(this.selectedClass);
32107                                 this.selections.remove(node);
32108                                 if(!suppressEvent){
32109                                         this.fireEvent("selectionchange", this, this.selections);
32110                                 }
32111                         }
32112                 }
32113     }
32114 });
32115 /*
32116  * Based on:
32117  * Ext JS Library 1.1.1
32118  * Copyright(c) 2006-2007, Ext JS, LLC.
32119  *
32120  * Originally Released Under LGPL - original licence link has changed is not relivant.
32121  *
32122  * Fork - LGPL
32123  * <script type="text/javascript">
32124  */
32125  
32126 /**
32127  * @class Roo.LayoutManager
32128  * @extends Roo.util.Observable
32129  * Base class for layout managers.
32130  */
32131 Roo.LayoutManager = function(container, config){
32132     Roo.LayoutManager.superclass.constructor.call(this);
32133     this.el = Roo.get(container);
32134     // ie scrollbar fix
32135     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32136         document.body.scroll = "no";
32137     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32138         this.el.position('relative');
32139     }
32140     this.id = this.el.id;
32141     this.el.addClass("x-layout-container");
32142     /** false to disable window resize monitoring @type Boolean */
32143     this.monitorWindowResize = true;
32144     this.regions = {};
32145     this.addEvents({
32146         /**
32147          * @event layout
32148          * Fires when a layout is performed. 
32149          * @param {Roo.LayoutManager} this
32150          */
32151         "layout" : true,
32152         /**
32153          * @event regionresized
32154          * Fires when the user resizes a region. 
32155          * @param {Roo.LayoutRegion} region The resized region
32156          * @param {Number} newSize The new size (width for east/west, height for north/south)
32157          */
32158         "regionresized" : true,
32159         /**
32160          * @event regioncollapsed
32161          * Fires when a region is collapsed. 
32162          * @param {Roo.LayoutRegion} region The collapsed region
32163          */
32164         "regioncollapsed" : true,
32165         /**
32166          * @event regionexpanded
32167          * Fires when a region is expanded.  
32168          * @param {Roo.LayoutRegion} region The expanded region
32169          */
32170         "regionexpanded" : true
32171     });
32172     this.updating = false;
32173     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32174 };
32175
32176 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32177     /**
32178      * Returns true if this layout is currently being updated
32179      * @return {Boolean}
32180      */
32181     isUpdating : function(){
32182         return this.updating; 
32183     },
32184     
32185     /**
32186      * Suspend the LayoutManager from doing auto-layouts while
32187      * making multiple add or remove calls
32188      */
32189     beginUpdate : function(){
32190         this.updating = true;    
32191     },
32192     
32193     /**
32194      * Restore auto-layouts and optionally disable the manager from performing a layout
32195      * @param {Boolean} noLayout true to disable a layout update 
32196      */
32197     endUpdate : function(noLayout){
32198         this.updating = false;
32199         if(!noLayout){
32200             this.layout();
32201         }    
32202     },
32203     
32204     layout: function(){
32205         
32206     },
32207     
32208     onRegionResized : function(region, newSize){
32209         this.fireEvent("regionresized", region, newSize);
32210         this.layout();
32211     },
32212     
32213     onRegionCollapsed : function(region){
32214         this.fireEvent("regioncollapsed", region);
32215     },
32216     
32217     onRegionExpanded : function(region){
32218         this.fireEvent("regionexpanded", region);
32219     },
32220         
32221     /**
32222      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32223      * performs box-model adjustments.
32224      * @return {Object} The size as an object {width: (the width), height: (the height)}
32225      */
32226     getViewSize : function(){
32227         var size;
32228         if(this.el.dom != document.body){
32229             size = this.el.getSize();
32230         }else{
32231             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32232         }
32233         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32234         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32235         return size;
32236     },
32237     
32238     /**
32239      * Returns the Element this layout is bound to.
32240      * @return {Roo.Element}
32241      */
32242     getEl : function(){
32243         return this.el;
32244     },
32245     
32246     /**
32247      * Returns the specified region.
32248      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32249      * @return {Roo.LayoutRegion}
32250      */
32251     getRegion : function(target){
32252         return this.regions[target.toLowerCase()];
32253     },
32254     
32255     onWindowResize : function(){
32256         if(this.monitorWindowResize){
32257             this.layout();
32258         }
32259     }
32260 });/*
32261  * Based on:
32262  * Ext JS Library 1.1.1
32263  * Copyright(c) 2006-2007, Ext JS, LLC.
32264  *
32265  * Originally Released Under LGPL - original licence link has changed is not relivant.
32266  *
32267  * Fork - LGPL
32268  * <script type="text/javascript">
32269  */
32270 /**
32271  * @class Roo.BorderLayout
32272  * @extends Roo.LayoutManager
32273  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32274  * please see: <br><br>
32275  * <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>
32276  * <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>
32277  * Example:
32278  <pre><code>
32279  var layout = new Roo.BorderLayout(document.body, {
32280     north: {
32281         initialSize: 25,
32282         titlebar: false
32283     },
32284     west: {
32285         split:true,
32286         initialSize: 200,
32287         minSize: 175,
32288         maxSize: 400,
32289         titlebar: true,
32290         collapsible: true
32291     },
32292     east: {
32293         split:true,
32294         initialSize: 202,
32295         minSize: 175,
32296         maxSize: 400,
32297         titlebar: true,
32298         collapsible: true
32299     },
32300     south: {
32301         split:true,
32302         initialSize: 100,
32303         minSize: 100,
32304         maxSize: 200,
32305         titlebar: true,
32306         collapsible: true
32307     },
32308     center: {
32309         titlebar: true,
32310         autoScroll:true,
32311         resizeTabs: true,
32312         minTabWidth: 50,
32313         preferredTabWidth: 150
32314     }
32315 });
32316
32317 // shorthand
32318 var CP = Roo.ContentPanel;
32319
32320 layout.beginUpdate();
32321 layout.add("north", new CP("north", "North"));
32322 layout.add("south", new CP("south", {title: "South", closable: true}));
32323 layout.add("west", new CP("west", {title: "West"}));
32324 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32325 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32326 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32327 layout.getRegion("center").showPanel("center1");
32328 layout.endUpdate();
32329 </code></pre>
32330
32331 <b>The container the layout is rendered into can be either the body element or any other element.
32332 If it is not the body element, the container needs to either be an absolute positioned element,
32333 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32334 the container size if it is not the body element.</b>
32335
32336 * @constructor
32337 * Create a new BorderLayout
32338 * @param {String/HTMLElement/Element} container The container this layout is bound to
32339 * @param {Object} config Configuration options
32340  */
32341 Roo.BorderLayout = function(container, config){
32342     config = config || {};
32343     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32344     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32345     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32346         var target = this.factory.validRegions[i];
32347         if(config[target]){
32348             this.addRegion(target, config[target]);
32349         }
32350     }
32351 };
32352
32353 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32354     /**
32355      * Creates and adds a new region if it doesn't already exist.
32356      * @param {String} target The target region key (north, south, east, west or center).
32357      * @param {Object} config The regions config object
32358      * @return {BorderLayoutRegion} The new region
32359      */
32360     addRegion : function(target, config){
32361         if(!this.regions[target]){
32362             var r = this.factory.create(target, this, config);
32363             this.bindRegion(target, r);
32364         }
32365         return this.regions[target];
32366     },
32367
32368     // private (kinda)
32369     bindRegion : function(name, r){
32370         this.regions[name] = r;
32371         r.on("visibilitychange", this.layout, this);
32372         r.on("paneladded", this.layout, this);
32373         r.on("panelremoved", this.layout, this);
32374         r.on("invalidated", this.layout, this);
32375         r.on("resized", this.onRegionResized, this);
32376         r.on("collapsed", this.onRegionCollapsed, this);
32377         r.on("expanded", this.onRegionExpanded, this);
32378     },
32379
32380     /**
32381      * Performs a layout update.
32382      */
32383     layout : function(){
32384         if(this.updating) return;
32385         var size = this.getViewSize();
32386         var w = size.width;
32387         var h = size.height;
32388         var centerW = w;
32389         var centerH = h;
32390         var centerY = 0;
32391         var centerX = 0;
32392         //var x = 0, y = 0;
32393
32394         var rs = this.regions;
32395         var north = rs["north"];
32396         var south = rs["south"]; 
32397         var west = rs["west"];
32398         var east = rs["east"];
32399         var center = rs["center"];
32400         //if(this.hideOnLayout){ // not supported anymore
32401             //c.el.setStyle("display", "none");
32402         //}
32403         if(north && north.isVisible()){
32404             var b = north.getBox();
32405             var m = north.getMargins();
32406             b.width = w - (m.left+m.right);
32407             b.x = m.left;
32408             b.y = m.top;
32409             centerY = b.height + b.y + m.bottom;
32410             centerH -= centerY;
32411             north.updateBox(this.safeBox(b));
32412         }
32413         if(south && south.isVisible()){
32414             var b = south.getBox();
32415             var m = south.getMargins();
32416             b.width = w - (m.left+m.right);
32417             b.x = m.left;
32418             var totalHeight = (b.height + m.top + m.bottom);
32419             b.y = h - totalHeight + m.top;
32420             centerH -= totalHeight;
32421             south.updateBox(this.safeBox(b));
32422         }
32423         if(west && west.isVisible()){
32424             var b = west.getBox();
32425             var m = west.getMargins();
32426             b.height = centerH - (m.top+m.bottom);
32427             b.x = m.left;
32428             b.y = centerY + m.top;
32429             var totalWidth = (b.width + m.left + m.right);
32430             centerX += totalWidth;
32431             centerW -= totalWidth;
32432             west.updateBox(this.safeBox(b));
32433         }
32434         if(east && east.isVisible()){
32435             var b = east.getBox();
32436             var m = east.getMargins();
32437             b.height = centerH - (m.top+m.bottom);
32438             var totalWidth = (b.width + m.left + m.right);
32439             b.x = w - totalWidth + m.left;
32440             b.y = centerY + m.top;
32441             centerW -= totalWidth;
32442             east.updateBox(this.safeBox(b));
32443         }
32444         if(center){
32445             var m = center.getMargins();
32446             var centerBox = {
32447                 x: centerX + m.left,
32448                 y: centerY + m.top,
32449                 width: centerW - (m.left+m.right),
32450                 height: centerH - (m.top+m.bottom)
32451             };
32452             //if(this.hideOnLayout){
32453                 //center.el.setStyle("display", "block");
32454             //}
32455             center.updateBox(this.safeBox(centerBox));
32456         }
32457         this.el.repaint();
32458         this.fireEvent("layout", this);
32459     },
32460
32461     // private
32462     safeBox : function(box){
32463         box.width = Math.max(0, box.width);
32464         box.height = Math.max(0, box.height);
32465         return box;
32466     },
32467
32468     /**
32469      * Adds a ContentPanel (or subclass) to this layout.
32470      * @param {String} target The target region key (north, south, east, west or center).
32471      * @param {Roo.ContentPanel} panel The panel to add
32472      * @return {Roo.ContentPanel} The added panel
32473      */
32474     add : function(target, panel){
32475          
32476         target = target.toLowerCase();
32477         return this.regions[target].add(panel);
32478     },
32479
32480     /**
32481      * Remove a ContentPanel (or subclass) to this layout.
32482      * @param {String} target The target region key (north, south, east, west or center).
32483      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32484      * @return {Roo.ContentPanel} The removed panel
32485      */
32486     remove : function(target, panel){
32487         target = target.toLowerCase();
32488         return this.regions[target].remove(panel);
32489     },
32490
32491     /**
32492      * Searches all regions for a panel with the specified id
32493      * @param {String} panelId
32494      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32495      */
32496     findPanel : function(panelId){
32497         var rs = this.regions;
32498         for(var target in rs){
32499             if(typeof rs[target] != "function"){
32500                 var p = rs[target].getPanel(panelId);
32501                 if(p){
32502                     return p;
32503                 }
32504             }
32505         }
32506         return null;
32507     },
32508
32509     /**
32510      * Searches all regions for a panel with the specified id and activates (shows) it.
32511      * @param {String/ContentPanel} panelId The panels id or the panel itself
32512      * @return {Roo.ContentPanel} The shown panel or null
32513      */
32514     showPanel : function(panelId) {
32515       var rs = this.regions;
32516       for(var target in rs){
32517          var r = rs[target];
32518          if(typeof r != "function"){
32519             if(r.hasPanel(panelId)){
32520                return r.showPanel(panelId);
32521             }
32522          }
32523       }
32524       return null;
32525    },
32526
32527    /**
32528      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32529      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32530      */
32531     restoreState : function(provider){
32532         if(!provider){
32533             provider = Roo.state.Manager;
32534         }
32535         var sm = new Roo.LayoutStateManager();
32536         sm.init(this, provider);
32537     },
32538
32539     /**
32540      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32541      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32542      * a valid ContentPanel config object.  Example:
32543      * <pre><code>
32544 // Create the main layout
32545 var layout = new Roo.BorderLayout('main-ct', {
32546     west: {
32547         split:true,
32548         minSize: 175,
32549         titlebar: true
32550     },
32551     center: {
32552         title:'Components'
32553     }
32554 }, 'main-ct');
32555
32556 // Create and add multiple ContentPanels at once via configs
32557 layout.batchAdd({
32558    west: {
32559        id: 'source-files',
32560        autoCreate:true,
32561        title:'Ext Source Files',
32562        autoScroll:true,
32563        fitToFrame:true
32564    },
32565    center : {
32566        el: cview,
32567        autoScroll:true,
32568        fitToFrame:true,
32569        toolbar: tb,
32570        resizeEl:'cbody'
32571    }
32572 });
32573 </code></pre>
32574      * @param {Object} regions An object containing ContentPanel configs by region name
32575      */
32576     batchAdd : function(regions){
32577         this.beginUpdate();
32578         for(var rname in regions){
32579             var lr = this.regions[rname];
32580             if(lr){
32581                 this.addTypedPanels(lr, regions[rname]);
32582             }
32583         }
32584         this.endUpdate();
32585     },
32586
32587     // private
32588     addTypedPanels : function(lr, ps){
32589         if(typeof ps == 'string'){
32590             lr.add(new Roo.ContentPanel(ps));
32591         }
32592         else if(ps instanceof Array){
32593             for(var i =0, len = ps.length; i < len; i++){
32594                 this.addTypedPanels(lr, ps[i]);
32595             }
32596         }
32597         else if(!ps.events){ // raw config?
32598             var el = ps.el;
32599             delete ps.el; // prevent conflict
32600             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32601         }
32602         else {  // panel object assumed!
32603             lr.add(ps);
32604         }
32605     },
32606     /**
32607      * Adds a xtype elements to the layout.
32608      * <pre><code>
32609
32610 layout.addxtype({
32611        xtype : 'ContentPanel',
32612        region: 'west',
32613        items: [ .... ]
32614    }
32615 );
32616
32617 layout.addxtype({
32618         xtype : 'NestedLayoutPanel',
32619         region: 'west',
32620         layout: {
32621            center: { },
32622            west: { }   
32623         },
32624         items : [ ... list of content panels or nested layout panels.. ]
32625    }
32626 );
32627 </code></pre>
32628      * @param {Object} cfg Xtype definition of item to add.
32629      */
32630     addxtype : function(cfg)
32631     {
32632         // basically accepts a pannel...
32633         // can accept a layout region..!?!?
32634         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32635         
32636         if (!cfg.xtype.match(/Panel$/)) {
32637             return false;
32638         }
32639         var ret = false;
32640         
32641         if (typeof(cfg.region) == 'undefined') {
32642             Roo.log("Failed to add Panel, region was not set");
32643             Roo.log(cfg);
32644             return false;
32645         }
32646         var region = cfg.region;
32647         delete cfg.region;
32648         
32649           
32650         var xitems = [];
32651         if (cfg.items) {
32652             xitems = cfg.items;
32653             delete cfg.items;
32654         }
32655         var nb = false;
32656         
32657         switch(cfg.xtype) 
32658         {
32659             case 'ContentPanel':  // ContentPanel (el, cfg)
32660             case 'ScrollPanel':  // ContentPanel (el, cfg)
32661             case 'ViewPanel': 
32662                 if(cfg.autoCreate) {
32663                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32664                 } else {
32665                     var el = this.el.createChild();
32666                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32667                 }
32668                 
32669                 this.add(region, ret);
32670                 break;
32671             
32672             
32673             case 'TreePanel': // our new panel!
32674                 cfg.el = this.el.createChild();
32675                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32676                 this.add(region, ret);
32677                 break;
32678             
32679             case 'NestedLayoutPanel': 
32680                 // create a new Layout (which is  a Border Layout...
32681                 var el = this.el.createChild();
32682                 var clayout = cfg.layout;
32683                 delete cfg.layout;
32684                 clayout.items   = clayout.items  || [];
32685                 // replace this exitems with the clayout ones..
32686                 xitems = clayout.items;
32687                  
32688                 
32689                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32690                     cfg.background = false;
32691                 }
32692                 var layout = new Roo.BorderLayout(el, clayout);
32693                 
32694                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32695                 //console.log('adding nested layout panel '  + cfg.toSource());
32696                 this.add(region, ret);
32697                 nb = {}; /// find first...
32698                 break;
32699                 
32700             case 'GridPanel': 
32701             
32702                 // needs grid and region
32703                 
32704                 //var el = this.getRegion(region).el.createChild();
32705                 var el = this.el.createChild();
32706                 // create the grid first...
32707                 
32708                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32709                 delete cfg.grid;
32710                 if (region == 'center' && this.active ) {
32711                     cfg.background = false;
32712                 }
32713                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32714                 
32715                 this.add(region, ret);
32716                 if (cfg.background) {
32717                     ret.on('activate', function(gp) {
32718                         if (!gp.grid.rendered) {
32719                             gp.grid.render();
32720                         }
32721                     });
32722                 } else {
32723                     grid.render();
32724                 }
32725                 break;
32726            
32727            
32728            
32729                 
32730                 
32731                 
32732             default:
32733                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32734                     
32735                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32736                     this.add(region, ret);
32737                 } else {
32738                 
32739                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32740                     return null;
32741                 }
32742                 
32743              // GridPanel (grid, cfg)
32744             
32745         }
32746         this.beginUpdate();
32747         // add children..
32748         var region = '';
32749         var abn = {};
32750         Roo.each(xitems, function(i)  {
32751             region = nb && i.region ? i.region : false;
32752             
32753             var add = ret.addxtype(i);
32754            
32755             if (region) {
32756                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32757                 if (!i.background) {
32758                     abn[region] = nb[region] ;
32759                 }
32760             }
32761             
32762         });
32763         this.endUpdate();
32764
32765         // make the last non-background panel active..
32766         //if (nb) { Roo.log(abn); }
32767         if (nb) {
32768             
32769             for(var r in abn) {
32770                 region = this.getRegion(r);
32771                 if (region) {
32772                     // tried using nb[r], but it does not work..
32773                      
32774                     region.showPanel(abn[r]);
32775                    
32776                 }
32777             }
32778         }
32779         return ret;
32780         
32781     }
32782 });
32783
32784 /**
32785  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32786  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32787  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32788  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32789  * <pre><code>
32790 // shorthand
32791 var CP = Roo.ContentPanel;
32792
32793 var layout = Roo.BorderLayout.create({
32794     north: {
32795         initialSize: 25,
32796         titlebar: false,
32797         panels: [new CP("north", "North")]
32798     },
32799     west: {
32800         split:true,
32801         initialSize: 200,
32802         minSize: 175,
32803         maxSize: 400,
32804         titlebar: true,
32805         collapsible: true,
32806         panels: [new CP("west", {title: "West"})]
32807     },
32808     east: {
32809         split:true,
32810         initialSize: 202,
32811         minSize: 175,
32812         maxSize: 400,
32813         titlebar: true,
32814         collapsible: true,
32815         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32816     },
32817     south: {
32818         split:true,
32819         initialSize: 100,
32820         minSize: 100,
32821         maxSize: 200,
32822         titlebar: true,
32823         collapsible: true,
32824         panels: [new CP("south", {title: "South", closable: true})]
32825     },
32826     center: {
32827         titlebar: true,
32828         autoScroll:true,
32829         resizeTabs: true,
32830         minTabWidth: 50,
32831         preferredTabWidth: 150,
32832         panels: [
32833             new CP("center1", {title: "Close Me", closable: true}),
32834             new CP("center2", {title: "Center Panel", closable: false})
32835         ]
32836     }
32837 }, document.body);
32838
32839 layout.getRegion("center").showPanel("center1");
32840 </code></pre>
32841  * @param config
32842  * @param targetEl
32843  */
32844 Roo.BorderLayout.create = function(config, targetEl){
32845     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32846     layout.beginUpdate();
32847     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32848     for(var j = 0, jlen = regions.length; j < jlen; j++){
32849         var lr = regions[j];
32850         if(layout.regions[lr] && config[lr].panels){
32851             var r = layout.regions[lr];
32852             var ps = config[lr].panels;
32853             layout.addTypedPanels(r, ps);
32854         }
32855     }
32856     layout.endUpdate();
32857     return layout;
32858 };
32859
32860 // private
32861 Roo.BorderLayout.RegionFactory = {
32862     // private
32863     validRegions : ["north","south","east","west","center"],
32864
32865     // private
32866     create : function(target, mgr, config){
32867         target = target.toLowerCase();
32868         if(config.lightweight || config.basic){
32869             return new Roo.BasicLayoutRegion(mgr, config, target);
32870         }
32871         switch(target){
32872             case "north":
32873                 return new Roo.NorthLayoutRegion(mgr, config);
32874             case "south":
32875                 return new Roo.SouthLayoutRegion(mgr, config);
32876             case "east":
32877                 return new Roo.EastLayoutRegion(mgr, config);
32878             case "west":
32879                 return new Roo.WestLayoutRegion(mgr, config);
32880             case "center":
32881                 return new Roo.CenterLayoutRegion(mgr, config);
32882         }
32883         throw 'Layout region "'+target+'" not supported.';
32884     }
32885 };/*
32886  * Based on:
32887  * Ext JS Library 1.1.1
32888  * Copyright(c) 2006-2007, Ext JS, LLC.
32889  *
32890  * Originally Released Under LGPL - original licence link has changed is not relivant.
32891  *
32892  * Fork - LGPL
32893  * <script type="text/javascript">
32894  */
32895  
32896 /**
32897  * @class Roo.BasicLayoutRegion
32898  * @extends Roo.util.Observable
32899  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32900  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32901  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32902  */
32903 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32904     this.mgr = mgr;
32905     this.position  = pos;
32906     this.events = {
32907         /**
32908          * @scope Roo.BasicLayoutRegion
32909          */
32910         
32911         /**
32912          * @event beforeremove
32913          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32914          * @param {Roo.LayoutRegion} this
32915          * @param {Roo.ContentPanel} panel The panel
32916          * @param {Object} e The cancel event object
32917          */
32918         "beforeremove" : true,
32919         /**
32920          * @event invalidated
32921          * Fires when the layout for this region is changed.
32922          * @param {Roo.LayoutRegion} this
32923          */
32924         "invalidated" : true,
32925         /**
32926          * @event visibilitychange
32927          * Fires when this region is shown or hidden 
32928          * @param {Roo.LayoutRegion} this
32929          * @param {Boolean} visibility true or false
32930          */
32931         "visibilitychange" : true,
32932         /**
32933          * @event paneladded
32934          * Fires when a panel is added. 
32935          * @param {Roo.LayoutRegion} this
32936          * @param {Roo.ContentPanel} panel The panel
32937          */
32938         "paneladded" : true,
32939         /**
32940          * @event panelremoved
32941          * Fires when a panel is removed. 
32942          * @param {Roo.LayoutRegion} this
32943          * @param {Roo.ContentPanel} panel The panel
32944          */
32945         "panelremoved" : true,
32946         /**
32947          * @event collapsed
32948          * Fires when this region is collapsed.
32949          * @param {Roo.LayoutRegion} this
32950          */
32951         "collapsed" : true,
32952         /**
32953          * @event expanded
32954          * Fires when this region is expanded.
32955          * @param {Roo.LayoutRegion} this
32956          */
32957         "expanded" : true,
32958         /**
32959          * @event slideshow
32960          * Fires when this region is slid into view.
32961          * @param {Roo.LayoutRegion} this
32962          */
32963         "slideshow" : true,
32964         /**
32965          * @event slidehide
32966          * Fires when this region slides out of view. 
32967          * @param {Roo.LayoutRegion} this
32968          */
32969         "slidehide" : true,
32970         /**
32971          * @event panelactivated
32972          * Fires when a panel is activated. 
32973          * @param {Roo.LayoutRegion} this
32974          * @param {Roo.ContentPanel} panel The activated panel
32975          */
32976         "panelactivated" : true,
32977         /**
32978          * @event resized
32979          * Fires when the user resizes this region. 
32980          * @param {Roo.LayoutRegion} this
32981          * @param {Number} newSize The new size (width for east/west, height for north/south)
32982          */
32983         "resized" : true
32984     };
32985     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32986     this.panels = new Roo.util.MixedCollection();
32987     this.panels.getKey = this.getPanelId.createDelegate(this);
32988     this.box = null;
32989     this.activePanel = null;
32990     // ensure listeners are added...
32991     
32992     if (config.listeners || config.events) {
32993         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
32994             listeners : config.listeners || {},
32995             events : config.events || {}
32996         });
32997     }
32998     
32999     if(skipConfig !== true){
33000         this.applyConfig(config);
33001     }
33002 };
33003
33004 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33005     getPanelId : function(p){
33006         return p.getId();
33007     },
33008     
33009     applyConfig : function(config){
33010         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33011         this.config = config;
33012         
33013     },
33014     
33015     /**
33016      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33017      * the width, for horizontal (north, south) the height.
33018      * @param {Number} newSize The new width or height
33019      */
33020     resizeTo : function(newSize){
33021         var el = this.el ? this.el :
33022                  (this.activePanel ? this.activePanel.getEl() : null);
33023         if(el){
33024             switch(this.position){
33025                 case "east":
33026                 case "west":
33027                     el.setWidth(newSize);
33028                     this.fireEvent("resized", this, newSize);
33029                 break;
33030                 case "north":
33031                 case "south":
33032                     el.setHeight(newSize);
33033                     this.fireEvent("resized", this, newSize);
33034                 break;                
33035             }
33036         }
33037     },
33038     
33039     getBox : function(){
33040         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33041     },
33042     
33043     getMargins : function(){
33044         return this.margins;
33045     },
33046     
33047     updateBox : function(box){
33048         this.box = box;
33049         var el = this.activePanel.getEl();
33050         el.dom.style.left = box.x + "px";
33051         el.dom.style.top = box.y + "px";
33052         this.activePanel.setSize(box.width, box.height);
33053     },
33054     
33055     /**
33056      * Returns the container element for this region.
33057      * @return {Roo.Element}
33058      */
33059     getEl : function(){
33060         return this.activePanel;
33061     },
33062     
33063     /**
33064      * Returns true if this region is currently visible.
33065      * @return {Boolean}
33066      */
33067     isVisible : function(){
33068         return this.activePanel ? true : false;
33069     },
33070     
33071     setActivePanel : function(panel){
33072         panel = this.getPanel(panel);
33073         if(this.activePanel && this.activePanel != panel){
33074             this.activePanel.setActiveState(false);
33075             this.activePanel.getEl().setLeftTop(-10000,-10000);
33076         }
33077         this.activePanel = panel;
33078         panel.setActiveState(true);
33079         if(this.box){
33080             panel.setSize(this.box.width, this.box.height);
33081         }
33082         this.fireEvent("panelactivated", this, panel);
33083         this.fireEvent("invalidated");
33084     },
33085     
33086     /**
33087      * Show the specified panel.
33088      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33089      * @return {Roo.ContentPanel} The shown panel or null
33090      */
33091     showPanel : function(panel){
33092         if(panel = this.getPanel(panel)){
33093             this.setActivePanel(panel);
33094         }
33095         return panel;
33096     },
33097     
33098     /**
33099      * Get the active panel for this region.
33100      * @return {Roo.ContentPanel} The active panel or null
33101      */
33102     getActivePanel : function(){
33103         return this.activePanel;
33104     },
33105     
33106     /**
33107      * Add the passed ContentPanel(s)
33108      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33109      * @return {Roo.ContentPanel} The panel added (if only one was added)
33110      */
33111     add : function(panel){
33112         if(arguments.length > 1){
33113             for(var i = 0, len = arguments.length; i < len; i++) {
33114                 this.add(arguments[i]);
33115             }
33116             return null;
33117         }
33118         if(this.hasPanel(panel)){
33119             this.showPanel(panel);
33120             return panel;
33121         }
33122         var el = panel.getEl();
33123         if(el.dom.parentNode != this.mgr.el.dom){
33124             this.mgr.el.dom.appendChild(el.dom);
33125         }
33126         if(panel.setRegion){
33127             panel.setRegion(this);
33128         }
33129         this.panels.add(panel);
33130         el.setStyle("position", "absolute");
33131         if(!panel.background){
33132             this.setActivePanel(panel);
33133             if(this.config.initialSize && this.panels.getCount()==1){
33134                 this.resizeTo(this.config.initialSize);
33135             }
33136         }
33137         this.fireEvent("paneladded", this, panel);
33138         return panel;
33139     },
33140     
33141     /**
33142      * Returns true if the panel is in this region.
33143      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33144      * @return {Boolean}
33145      */
33146     hasPanel : function(panel){
33147         if(typeof panel == "object"){ // must be panel obj
33148             panel = panel.getId();
33149         }
33150         return this.getPanel(panel) ? true : false;
33151     },
33152     
33153     /**
33154      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33155      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33156      * @param {Boolean} preservePanel Overrides the config preservePanel option
33157      * @return {Roo.ContentPanel} The panel that was removed
33158      */
33159     remove : function(panel, preservePanel){
33160         panel = this.getPanel(panel);
33161         if(!panel){
33162             return null;
33163         }
33164         var e = {};
33165         this.fireEvent("beforeremove", this, panel, e);
33166         if(e.cancel === true){
33167             return null;
33168         }
33169         var panelId = panel.getId();
33170         this.panels.removeKey(panelId);
33171         return panel;
33172     },
33173     
33174     /**
33175      * Returns the panel specified or null if it's not in this region.
33176      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33177      * @return {Roo.ContentPanel}
33178      */
33179     getPanel : function(id){
33180         if(typeof id == "object"){ // must be panel obj
33181             return id;
33182         }
33183         return this.panels.get(id);
33184     },
33185     
33186     /**
33187      * Returns this regions position (north/south/east/west/center).
33188      * @return {String} 
33189      */
33190     getPosition: function(){
33191         return this.position;    
33192     }
33193 });/*
33194  * Based on:
33195  * Ext JS Library 1.1.1
33196  * Copyright(c) 2006-2007, Ext JS, LLC.
33197  *
33198  * Originally Released Under LGPL - original licence link has changed is not relivant.
33199  *
33200  * Fork - LGPL
33201  * <script type="text/javascript">
33202  */
33203  
33204 /**
33205  * @class Roo.LayoutRegion
33206  * @extends Roo.BasicLayoutRegion
33207  * This class represents a region in a layout manager.
33208  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33209  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33210  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33211  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33212  * @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})
33213  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33214  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33215  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33216  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33217  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33218  * @cfg {String}    title           The title for the region (overrides panel titles)
33219  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33220  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33221  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33222  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33223  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33224  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33225  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33226  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33227  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33228  * @cfg {Boolean}   showPin         True to show a pin button
33229  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33230  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33231  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33232  * @cfg {Number}    width           For East/West panels
33233  * @cfg {Number}    height          For North/South panels
33234  * @cfg {Boolean}   split           To show the splitter
33235  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33236  */
33237 Roo.LayoutRegion = function(mgr, config, pos){
33238     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33239     var dh = Roo.DomHelper;
33240     /** This region's container element 
33241     * @type Roo.Element */
33242     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33243     /** This region's title element 
33244     * @type Roo.Element */
33245
33246     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33247         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33248         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33249     ]}, true);
33250     this.titleEl.enableDisplayMode();
33251     /** This region's title text element 
33252     * @type HTMLElement */
33253     this.titleTextEl = this.titleEl.dom.firstChild;
33254     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33255     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33256     this.closeBtn.enableDisplayMode();
33257     this.closeBtn.on("click", this.closeClicked, this);
33258     this.closeBtn.hide();
33259
33260     this.createBody(config);
33261     this.visible = true;
33262     this.collapsed = false;
33263
33264     if(config.hideWhenEmpty){
33265         this.hide();
33266         this.on("paneladded", this.validateVisibility, this);
33267         this.on("panelremoved", this.validateVisibility, this);
33268     }
33269     this.applyConfig(config);
33270 };
33271
33272 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33273
33274     createBody : function(){
33275         /** This region's body element 
33276         * @type Roo.Element */
33277         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33278     },
33279
33280     applyConfig : function(c){
33281         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33282             var dh = Roo.DomHelper;
33283             if(c.titlebar !== false){
33284                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33285                 this.collapseBtn.on("click", this.collapse, this);
33286                 this.collapseBtn.enableDisplayMode();
33287
33288                 if(c.showPin === true || this.showPin){
33289                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33290                     this.stickBtn.enableDisplayMode();
33291                     this.stickBtn.on("click", this.expand, this);
33292                     this.stickBtn.hide();
33293                 }
33294             }
33295             /** This region's collapsed element
33296             * @type Roo.Element */
33297             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33298                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33299             ]}, true);
33300             if(c.floatable !== false){
33301                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33302                this.collapsedEl.on("click", this.collapseClick, this);
33303             }
33304
33305             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33306                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33307                    id: "message", unselectable: "on", style:{"float":"left"}});
33308                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33309              }
33310             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33311             this.expandBtn.on("click", this.expand, this);
33312         }
33313         if(this.collapseBtn){
33314             this.collapseBtn.setVisible(c.collapsible == true);
33315         }
33316         this.cmargins = c.cmargins || this.cmargins ||
33317                          (this.position == "west" || this.position == "east" ?
33318                              {top: 0, left: 2, right:2, bottom: 0} :
33319                              {top: 2, left: 0, right:0, bottom: 2});
33320         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33321         this.bottomTabs = c.tabPosition != "top";
33322         this.autoScroll = c.autoScroll || false;
33323         if(this.autoScroll){
33324             this.bodyEl.setStyle("overflow", "auto");
33325         }else{
33326             this.bodyEl.setStyle("overflow", "hidden");
33327         }
33328         //if(c.titlebar !== false){
33329             if((!c.titlebar && !c.title) || c.titlebar === false){
33330                 this.titleEl.hide();
33331             }else{
33332                 this.titleEl.show();
33333                 if(c.title){
33334                     this.titleTextEl.innerHTML = c.title;
33335                 }
33336             }
33337         //}
33338         this.duration = c.duration || .30;
33339         this.slideDuration = c.slideDuration || .45;
33340         this.config = c;
33341         if(c.collapsed){
33342             this.collapse(true);
33343         }
33344         if(c.hidden){
33345             this.hide();
33346         }
33347     },
33348     /**
33349      * Returns true if this region is currently visible.
33350      * @return {Boolean}
33351      */
33352     isVisible : function(){
33353         return this.visible;
33354     },
33355
33356     /**
33357      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33358      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33359      */
33360     setCollapsedTitle : function(title){
33361         title = title || "&#160;";
33362         if(this.collapsedTitleTextEl){
33363             this.collapsedTitleTextEl.innerHTML = title;
33364         }
33365     },
33366
33367     getBox : function(){
33368         var b;
33369         if(!this.collapsed){
33370             b = this.el.getBox(false, true);
33371         }else{
33372             b = this.collapsedEl.getBox(false, true);
33373         }
33374         return b;
33375     },
33376
33377     getMargins : function(){
33378         return this.collapsed ? this.cmargins : this.margins;
33379     },
33380
33381     highlight : function(){
33382         this.el.addClass("x-layout-panel-dragover");
33383     },
33384
33385     unhighlight : function(){
33386         this.el.removeClass("x-layout-panel-dragover");
33387     },
33388
33389     updateBox : function(box){
33390         this.box = box;
33391         if(!this.collapsed){
33392             this.el.dom.style.left = box.x + "px";
33393             this.el.dom.style.top = box.y + "px";
33394             this.updateBody(box.width, box.height);
33395         }else{
33396             this.collapsedEl.dom.style.left = box.x + "px";
33397             this.collapsedEl.dom.style.top = box.y + "px";
33398             this.collapsedEl.setSize(box.width, box.height);
33399         }
33400         if(this.tabs){
33401             this.tabs.autoSizeTabs();
33402         }
33403     },
33404
33405     updateBody : function(w, h){
33406         if(w !== null){
33407             this.el.setWidth(w);
33408             w -= this.el.getBorderWidth("rl");
33409             if(this.config.adjustments){
33410                 w += this.config.adjustments[0];
33411             }
33412         }
33413         if(h !== null){
33414             this.el.setHeight(h);
33415             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33416             h -= this.el.getBorderWidth("tb");
33417             if(this.config.adjustments){
33418                 h += this.config.adjustments[1];
33419             }
33420             this.bodyEl.setHeight(h);
33421             if(this.tabs){
33422                 h = this.tabs.syncHeight(h);
33423             }
33424         }
33425         if(this.panelSize){
33426             w = w !== null ? w : this.panelSize.width;
33427             h = h !== null ? h : this.panelSize.height;
33428         }
33429         if(this.activePanel){
33430             var el = this.activePanel.getEl();
33431             w = w !== null ? w : el.getWidth();
33432             h = h !== null ? h : el.getHeight();
33433             this.panelSize = {width: w, height: h};
33434             this.activePanel.setSize(w, h);
33435         }
33436         if(Roo.isIE && this.tabs){
33437             this.tabs.el.repaint();
33438         }
33439     },
33440
33441     /**
33442      * Returns the container element for this region.
33443      * @return {Roo.Element}
33444      */
33445     getEl : function(){
33446         return this.el;
33447     },
33448
33449     /**
33450      * Hides this region.
33451      */
33452     hide : function(){
33453         if(!this.collapsed){
33454             this.el.dom.style.left = "-2000px";
33455             this.el.hide();
33456         }else{
33457             this.collapsedEl.dom.style.left = "-2000px";
33458             this.collapsedEl.hide();
33459         }
33460         this.visible = false;
33461         this.fireEvent("visibilitychange", this, false);
33462     },
33463
33464     /**
33465      * Shows this region if it was previously hidden.
33466      */
33467     show : function(){
33468         if(!this.collapsed){
33469             this.el.show();
33470         }else{
33471             this.collapsedEl.show();
33472         }
33473         this.visible = true;
33474         this.fireEvent("visibilitychange", this, true);
33475     },
33476
33477     closeClicked : function(){
33478         if(this.activePanel){
33479             this.remove(this.activePanel);
33480         }
33481     },
33482
33483     collapseClick : function(e){
33484         if(this.isSlid){
33485            e.stopPropagation();
33486            this.slideIn();
33487         }else{
33488            e.stopPropagation();
33489            this.slideOut();
33490         }
33491     },
33492
33493     /**
33494      * Collapses this region.
33495      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33496      */
33497     collapse : function(skipAnim){
33498         if(this.collapsed) return;
33499         this.collapsed = true;
33500         if(this.split){
33501             this.split.el.hide();
33502         }
33503         if(this.config.animate && skipAnim !== true){
33504             this.fireEvent("invalidated", this);
33505             this.animateCollapse();
33506         }else{
33507             this.el.setLocation(-20000,-20000);
33508             this.el.hide();
33509             this.collapsedEl.show();
33510             this.fireEvent("collapsed", this);
33511             this.fireEvent("invalidated", this);
33512         }
33513     },
33514
33515     animateCollapse : function(){
33516         // overridden
33517     },
33518
33519     /**
33520      * Expands this region if it was previously collapsed.
33521      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33522      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33523      */
33524     expand : function(e, skipAnim){
33525         if(e) e.stopPropagation();
33526         if(!this.collapsed || this.el.hasActiveFx()) return;
33527         if(this.isSlid){
33528             this.afterSlideIn();
33529             skipAnim = true;
33530         }
33531         this.collapsed = false;
33532         if(this.config.animate && skipAnim !== true){
33533             this.animateExpand();
33534         }else{
33535             this.el.show();
33536             if(this.split){
33537                 this.split.el.show();
33538             }
33539             this.collapsedEl.setLocation(-2000,-2000);
33540             this.collapsedEl.hide();
33541             this.fireEvent("invalidated", this);
33542             this.fireEvent("expanded", this);
33543         }
33544     },
33545
33546     animateExpand : function(){
33547         // overridden
33548     },
33549
33550     initTabs : function()
33551     {
33552         this.bodyEl.setStyle("overflow", "hidden");
33553         var ts = new Roo.TabPanel(
33554                 this.bodyEl.dom,
33555                 {
33556                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33557                     disableTooltips: this.config.disableTabTips,
33558                     toolbar : this.config.toolbar
33559                 }
33560         );
33561         if(this.config.hideTabs){
33562             ts.stripWrap.setDisplayed(false);
33563         }
33564         this.tabs = ts;
33565         ts.resizeTabs = this.config.resizeTabs === true;
33566         ts.minTabWidth = this.config.minTabWidth || 40;
33567         ts.maxTabWidth = this.config.maxTabWidth || 250;
33568         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33569         ts.monitorResize = false;
33570         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33571         ts.bodyEl.addClass('x-layout-tabs-body');
33572         this.panels.each(this.initPanelAsTab, this);
33573     },
33574
33575     initPanelAsTab : function(panel){
33576         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33577                     this.config.closeOnTab && panel.isClosable());
33578         if(panel.tabTip !== undefined){
33579             ti.setTooltip(panel.tabTip);
33580         }
33581         ti.on("activate", function(){
33582               this.setActivePanel(panel);
33583         }, this);
33584         if(this.config.closeOnTab){
33585             ti.on("beforeclose", function(t, e){
33586                 e.cancel = true;
33587                 this.remove(panel);
33588             }, this);
33589         }
33590         return ti;
33591     },
33592
33593     updatePanelTitle : function(panel, title){
33594         if(this.activePanel == panel){
33595             this.updateTitle(title);
33596         }
33597         if(this.tabs){
33598             var ti = this.tabs.getTab(panel.getEl().id);
33599             ti.setText(title);
33600             if(panel.tabTip !== undefined){
33601                 ti.setTooltip(panel.tabTip);
33602             }
33603         }
33604     },
33605
33606     updateTitle : function(title){
33607         if(this.titleTextEl && !this.config.title){
33608             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33609         }
33610     },
33611
33612     setActivePanel : function(panel){
33613         panel = this.getPanel(panel);
33614         if(this.activePanel && this.activePanel != panel){
33615             this.activePanel.setActiveState(false);
33616         }
33617         this.activePanel = panel;
33618         panel.setActiveState(true);
33619         if(this.panelSize){
33620             panel.setSize(this.panelSize.width, this.panelSize.height);
33621         }
33622         if(this.closeBtn){
33623             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33624         }
33625         this.updateTitle(panel.getTitle());
33626         if(this.tabs){
33627             this.fireEvent("invalidated", this);
33628         }
33629         this.fireEvent("panelactivated", this, panel);
33630     },
33631
33632     /**
33633      * Shows the specified panel.
33634      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33635      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33636      */
33637     showPanel : function(panel){
33638         if(panel = this.getPanel(panel)){
33639             if(this.tabs){
33640                 var tab = this.tabs.getTab(panel.getEl().id);
33641                 if(tab.isHidden()){
33642                     this.tabs.unhideTab(tab.id);
33643                 }
33644                 tab.activate();
33645             }else{
33646                 this.setActivePanel(panel);
33647             }
33648         }
33649         return panel;
33650     },
33651
33652     /**
33653      * Get the active panel for this region.
33654      * @return {Roo.ContentPanel} The active panel or null
33655      */
33656     getActivePanel : function(){
33657         return this.activePanel;
33658     },
33659
33660     validateVisibility : function(){
33661         if(this.panels.getCount() < 1){
33662             this.updateTitle("&#160;");
33663             this.closeBtn.hide();
33664             this.hide();
33665         }else{
33666             if(!this.isVisible()){
33667                 this.show();
33668             }
33669         }
33670     },
33671
33672     /**
33673      * Adds the passed ContentPanel(s) to this region.
33674      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33675      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33676      */
33677     add : function(panel){
33678         if(arguments.length > 1){
33679             for(var i = 0, len = arguments.length; i < len; i++) {
33680                 this.add(arguments[i]);
33681             }
33682             return null;
33683         }
33684         if(this.hasPanel(panel)){
33685             this.showPanel(panel);
33686             return panel;
33687         }
33688         panel.setRegion(this);
33689         this.panels.add(panel);
33690         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33691             this.bodyEl.dom.appendChild(panel.getEl().dom);
33692             if(panel.background !== true){
33693                 this.setActivePanel(panel);
33694             }
33695             this.fireEvent("paneladded", this, panel);
33696             return panel;
33697         }
33698         if(!this.tabs){
33699             this.initTabs();
33700         }else{
33701             this.initPanelAsTab(panel);
33702         }
33703         if(panel.background !== true){
33704             this.tabs.activate(panel.getEl().id);
33705         }
33706         this.fireEvent("paneladded", this, panel);
33707         return panel;
33708     },
33709
33710     /**
33711      * Hides the tab for the specified panel.
33712      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33713      */
33714     hidePanel : function(panel){
33715         if(this.tabs && (panel = this.getPanel(panel))){
33716             this.tabs.hideTab(panel.getEl().id);
33717         }
33718     },
33719
33720     /**
33721      * Unhides the tab for a previously hidden panel.
33722      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33723      */
33724     unhidePanel : function(panel){
33725         if(this.tabs && (panel = this.getPanel(panel))){
33726             this.tabs.unhideTab(panel.getEl().id);
33727         }
33728     },
33729
33730     clearPanels : function(){
33731         while(this.panels.getCount() > 0){
33732              this.remove(this.panels.first());
33733         }
33734     },
33735
33736     /**
33737      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33738      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33739      * @param {Boolean} preservePanel Overrides the config preservePanel option
33740      * @return {Roo.ContentPanel} The panel that was removed
33741      */
33742     remove : function(panel, preservePanel){
33743         panel = this.getPanel(panel);
33744         if(!panel){
33745             return null;
33746         }
33747         var e = {};
33748         this.fireEvent("beforeremove", this, panel, e);
33749         if(e.cancel === true){
33750             return null;
33751         }
33752         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33753         var panelId = panel.getId();
33754         this.panels.removeKey(panelId);
33755         if(preservePanel){
33756             document.body.appendChild(panel.getEl().dom);
33757         }
33758         if(this.tabs){
33759             this.tabs.removeTab(panel.getEl().id);
33760         }else if (!preservePanel){
33761             this.bodyEl.dom.removeChild(panel.getEl().dom);
33762         }
33763         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33764             var p = this.panels.first();
33765             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33766             tempEl.appendChild(p.getEl().dom);
33767             this.bodyEl.update("");
33768             this.bodyEl.dom.appendChild(p.getEl().dom);
33769             tempEl = null;
33770             this.updateTitle(p.getTitle());
33771             this.tabs = null;
33772             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33773             this.setActivePanel(p);
33774         }
33775         panel.setRegion(null);
33776         if(this.activePanel == panel){
33777             this.activePanel = null;
33778         }
33779         if(this.config.autoDestroy !== false && preservePanel !== true){
33780             try{panel.destroy();}catch(e){}
33781         }
33782         this.fireEvent("panelremoved", this, panel);
33783         return panel;
33784     },
33785
33786     /**
33787      * Returns the TabPanel component used by this region
33788      * @return {Roo.TabPanel}
33789      */
33790     getTabs : function(){
33791         return this.tabs;
33792     },
33793
33794     createTool : function(parentEl, className){
33795         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33796             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33797         btn.addClassOnOver("x-layout-tools-button-over");
33798         return btn;
33799     }
33800 });/*
33801  * Based on:
33802  * Ext JS Library 1.1.1
33803  * Copyright(c) 2006-2007, Ext JS, LLC.
33804  *
33805  * Originally Released Under LGPL - original licence link has changed is not relivant.
33806  *
33807  * Fork - LGPL
33808  * <script type="text/javascript">
33809  */
33810  
33811
33812
33813 /**
33814  * @class Roo.SplitLayoutRegion
33815  * @extends Roo.LayoutRegion
33816  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33817  */
33818 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33819     this.cursor = cursor;
33820     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33821 };
33822
33823 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33824     splitTip : "Drag to resize.",
33825     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33826     useSplitTips : false,
33827
33828     applyConfig : function(config){
33829         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33830         if(config.split){
33831             if(!this.split){
33832                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33833                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33834                 /** The SplitBar for this region 
33835                 * @type Roo.SplitBar */
33836                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33837                 this.split.on("moved", this.onSplitMove, this);
33838                 this.split.useShim = config.useShim === true;
33839                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33840                 if(this.useSplitTips){
33841                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33842                 }
33843                 if(config.collapsible){
33844                     this.split.el.on("dblclick", this.collapse,  this);
33845                 }
33846             }
33847             if(typeof config.minSize != "undefined"){
33848                 this.split.minSize = config.minSize;
33849             }
33850             if(typeof config.maxSize != "undefined"){
33851                 this.split.maxSize = config.maxSize;
33852             }
33853             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33854                 this.hideSplitter();
33855             }
33856         }
33857     },
33858
33859     getHMaxSize : function(){
33860          var cmax = this.config.maxSize || 10000;
33861          var center = this.mgr.getRegion("center");
33862          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33863     },
33864
33865     getVMaxSize : function(){
33866          var cmax = this.config.maxSize || 10000;
33867          var center = this.mgr.getRegion("center");
33868          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33869     },
33870
33871     onSplitMove : function(split, newSize){
33872         this.fireEvent("resized", this, newSize);
33873     },
33874     
33875     /** 
33876      * Returns the {@link Roo.SplitBar} for this region.
33877      * @return {Roo.SplitBar}
33878      */
33879     getSplitBar : function(){
33880         return this.split;
33881     },
33882     
33883     hide : function(){
33884         this.hideSplitter();
33885         Roo.SplitLayoutRegion.superclass.hide.call(this);
33886     },
33887
33888     hideSplitter : function(){
33889         if(this.split){
33890             this.split.el.setLocation(-2000,-2000);
33891             this.split.el.hide();
33892         }
33893     },
33894
33895     show : function(){
33896         if(this.split){
33897             this.split.el.show();
33898         }
33899         Roo.SplitLayoutRegion.superclass.show.call(this);
33900     },
33901     
33902     beforeSlide: function(){
33903         if(Roo.isGecko){// firefox overflow auto bug workaround
33904             this.bodyEl.clip();
33905             if(this.tabs) this.tabs.bodyEl.clip();
33906             if(this.activePanel){
33907                 this.activePanel.getEl().clip();
33908                 
33909                 if(this.activePanel.beforeSlide){
33910                     this.activePanel.beforeSlide();
33911                 }
33912             }
33913         }
33914     },
33915     
33916     afterSlide : function(){
33917         if(Roo.isGecko){// firefox overflow auto bug workaround
33918             this.bodyEl.unclip();
33919             if(this.tabs) this.tabs.bodyEl.unclip();
33920             if(this.activePanel){
33921                 this.activePanel.getEl().unclip();
33922                 if(this.activePanel.afterSlide){
33923                     this.activePanel.afterSlide();
33924                 }
33925             }
33926         }
33927     },
33928
33929     initAutoHide : function(){
33930         if(this.autoHide !== false){
33931             if(!this.autoHideHd){
33932                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33933                 this.autoHideHd = {
33934                     "mouseout": function(e){
33935                         if(!e.within(this.el, true)){
33936                             st.delay(500);
33937                         }
33938                     },
33939                     "mouseover" : function(e){
33940                         st.cancel();
33941                     },
33942                     scope : this
33943                 };
33944             }
33945             this.el.on(this.autoHideHd);
33946         }
33947     },
33948
33949     clearAutoHide : function(){
33950         if(this.autoHide !== false){
33951             this.el.un("mouseout", this.autoHideHd.mouseout);
33952             this.el.un("mouseover", this.autoHideHd.mouseover);
33953         }
33954     },
33955
33956     clearMonitor : function(){
33957         Roo.get(document).un("click", this.slideInIf, this);
33958     },
33959
33960     // these names are backwards but not changed for compat
33961     slideOut : function(){
33962         if(this.isSlid || this.el.hasActiveFx()){
33963             return;
33964         }
33965         this.isSlid = true;
33966         if(this.collapseBtn){
33967             this.collapseBtn.hide();
33968         }
33969         this.closeBtnState = this.closeBtn.getStyle('display');
33970         this.closeBtn.hide();
33971         if(this.stickBtn){
33972             this.stickBtn.show();
33973         }
33974         this.el.show();
33975         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33976         this.beforeSlide();
33977         this.el.setStyle("z-index", 10001);
33978         this.el.slideIn(this.getSlideAnchor(), {
33979             callback: function(){
33980                 this.afterSlide();
33981                 this.initAutoHide();
33982                 Roo.get(document).on("click", this.slideInIf, this);
33983                 this.fireEvent("slideshow", this);
33984             },
33985             scope: this,
33986             block: true
33987         });
33988     },
33989
33990     afterSlideIn : function(){
33991         this.clearAutoHide();
33992         this.isSlid = false;
33993         this.clearMonitor();
33994         this.el.setStyle("z-index", "");
33995         if(this.collapseBtn){
33996             this.collapseBtn.show();
33997         }
33998         this.closeBtn.setStyle('display', this.closeBtnState);
33999         if(this.stickBtn){
34000             this.stickBtn.hide();
34001         }
34002         this.fireEvent("slidehide", this);
34003     },
34004
34005     slideIn : function(cb){
34006         if(!this.isSlid || this.el.hasActiveFx()){
34007             Roo.callback(cb);
34008             return;
34009         }
34010         this.isSlid = false;
34011         this.beforeSlide();
34012         this.el.slideOut(this.getSlideAnchor(), {
34013             callback: function(){
34014                 this.el.setLeftTop(-10000, -10000);
34015                 this.afterSlide();
34016                 this.afterSlideIn();
34017                 Roo.callback(cb);
34018             },
34019             scope: this,
34020             block: true
34021         });
34022     },
34023     
34024     slideInIf : function(e){
34025         if(!e.within(this.el)){
34026             this.slideIn();
34027         }
34028     },
34029
34030     animateCollapse : function(){
34031         this.beforeSlide();
34032         this.el.setStyle("z-index", 20000);
34033         var anchor = this.getSlideAnchor();
34034         this.el.slideOut(anchor, {
34035             callback : function(){
34036                 this.el.setStyle("z-index", "");
34037                 this.collapsedEl.slideIn(anchor, {duration:.3});
34038                 this.afterSlide();
34039                 this.el.setLocation(-10000,-10000);
34040                 this.el.hide();
34041                 this.fireEvent("collapsed", this);
34042             },
34043             scope: this,
34044             block: true
34045         });
34046     },
34047
34048     animateExpand : function(){
34049         this.beforeSlide();
34050         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34051         this.el.setStyle("z-index", 20000);
34052         this.collapsedEl.hide({
34053             duration:.1
34054         });
34055         this.el.slideIn(this.getSlideAnchor(), {
34056             callback : function(){
34057                 this.el.setStyle("z-index", "");
34058                 this.afterSlide();
34059                 if(this.split){
34060                     this.split.el.show();
34061                 }
34062                 this.fireEvent("invalidated", this);
34063                 this.fireEvent("expanded", this);
34064             },
34065             scope: this,
34066             block: true
34067         });
34068     },
34069
34070     anchors : {
34071         "west" : "left",
34072         "east" : "right",
34073         "north" : "top",
34074         "south" : "bottom"
34075     },
34076
34077     sanchors : {
34078         "west" : "l",
34079         "east" : "r",
34080         "north" : "t",
34081         "south" : "b"
34082     },
34083
34084     canchors : {
34085         "west" : "tl-tr",
34086         "east" : "tr-tl",
34087         "north" : "tl-bl",
34088         "south" : "bl-tl"
34089     },
34090
34091     getAnchor : function(){
34092         return this.anchors[this.position];
34093     },
34094
34095     getCollapseAnchor : function(){
34096         return this.canchors[this.position];
34097     },
34098
34099     getSlideAnchor : function(){
34100         return this.sanchors[this.position];
34101     },
34102
34103     getAlignAdj : function(){
34104         var cm = this.cmargins;
34105         switch(this.position){
34106             case "west":
34107                 return [0, 0];
34108             break;
34109             case "east":
34110                 return [0, 0];
34111             break;
34112             case "north":
34113                 return [0, 0];
34114             break;
34115             case "south":
34116                 return [0, 0];
34117             break;
34118         }
34119     },
34120
34121     getExpandAdj : function(){
34122         var c = this.collapsedEl, cm = this.cmargins;
34123         switch(this.position){
34124             case "west":
34125                 return [-(cm.right+c.getWidth()+cm.left), 0];
34126             break;
34127             case "east":
34128                 return [cm.right+c.getWidth()+cm.left, 0];
34129             break;
34130             case "north":
34131                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34132             break;
34133             case "south":
34134                 return [0, cm.top+cm.bottom+c.getHeight()];
34135             break;
34136         }
34137     }
34138 });/*
34139  * Based on:
34140  * Ext JS Library 1.1.1
34141  * Copyright(c) 2006-2007, Ext JS, LLC.
34142  *
34143  * Originally Released Under LGPL - original licence link has changed is not relivant.
34144  *
34145  * Fork - LGPL
34146  * <script type="text/javascript">
34147  */
34148 /*
34149  * These classes are private internal classes
34150  */
34151 Roo.CenterLayoutRegion = function(mgr, config){
34152     Roo.LayoutRegion.call(this, mgr, config, "center");
34153     this.visible = true;
34154     this.minWidth = config.minWidth || 20;
34155     this.minHeight = config.minHeight || 20;
34156 };
34157
34158 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34159     hide : function(){
34160         // center panel can't be hidden
34161     },
34162     
34163     show : function(){
34164         // center panel can't be hidden
34165     },
34166     
34167     getMinWidth: function(){
34168         return this.minWidth;
34169     },
34170     
34171     getMinHeight: function(){
34172         return this.minHeight;
34173     }
34174 });
34175
34176
34177 Roo.NorthLayoutRegion = function(mgr, config){
34178     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34179     if(this.split){
34180         this.split.placement = Roo.SplitBar.TOP;
34181         this.split.orientation = Roo.SplitBar.VERTICAL;
34182         this.split.el.addClass("x-layout-split-v");
34183     }
34184     var size = config.initialSize || config.height;
34185     if(typeof size != "undefined"){
34186         this.el.setHeight(size);
34187     }
34188 };
34189 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34190     orientation: Roo.SplitBar.VERTICAL,
34191     getBox : function(){
34192         if(this.collapsed){
34193             return this.collapsedEl.getBox();
34194         }
34195         var box = this.el.getBox();
34196         if(this.split){
34197             box.height += this.split.el.getHeight();
34198         }
34199         return box;
34200     },
34201     
34202     updateBox : function(box){
34203         if(this.split && !this.collapsed){
34204             box.height -= this.split.el.getHeight();
34205             this.split.el.setLeft(box.x);
34206             this.split.el.setTop(box.y+box.height);
34207             this.split.el.setWidth(box.width);
34208         }
34209         if(this.collapsed){
34210             this.updateBody(box.width, null);
34211         }
34212         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34213     }
34214 });
34215
34216 Roo.SouthLayoutRegion = function(mgr, config){
34217     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34218     if(this.split){
34219         this.split.placement = Roo.SplitBar.BOTTOM;
34220         this.split.orientation = Roo.SplitBar.VERTICAL;
34221         this.split.el.addClass("x-layout-split-v");
34222     }
34223     var size = config.initialSize || config.height;
34224     if(typeof size != "undefined"){
34225         this.el.setHeight(size);
34226     }
34227 };
34228 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34229     orientation: Roo.SplitBar.VERTICAL,
34230     getBox : function(){
34231         if(this.collapsed){
34232             return this.collapsedEl.getBox();
34233         }
34234         var box = this.el.getBox();
34235         if(this.split){
34236             var sh = this.split.el.getHeight();
34237             box.height += sh;
34238             box.y -= sh;
34239         }
34240         return box;
34241     },
34242     
34243     updateBox : function(box){
34244         if(this.split && !this.collapsed){
34245             var sh = this.split.el.getHeight();
34246             box.height -= sh;
34247             box.y += sh;
34248             this.split.el.setLeft(box.x);
34249             this.split.el.setTop(box.y-sh);
34250             this.split.el.setWidth(box.width);
34251         }
34252         if(this.collapsed){
34253             this.updateBody(box.width, null);
34254         }
34255         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34256     }
34257 });
34258
34259 Roo.EastLayoutRegion = function(mgr, config){
34260     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34261     if(this.split){
34262         this.split.placement = Roo.SplitBar.RIGHT;
34263         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34264         this.split.el.addClass("x-layout-split-h");
34265     }
34266     var size = config.initialSize || config.width;
34267     if(typeof size != "undefined"){
34268         this.el.setWidth(size);
34269     }
34270 };
34271 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34272     orientation: Roo.SplitBar.HORIZONTAL,
34273     getBox : function(){
34274         if(this.collapsed){
34275             return this.collapsedEl.getBox();
34276         }
34277         var box = this.el.getBox();
34278         if(this.split){
34279             var sw = this.split.el.getWidth();
34280             box.width += sw;
34281             box.x -= sw;
34282         }
34283         return box;
34284     },
34285
34286     updateBox : function(box){
34287         if(this.split && !this.collapsed){
34288             var sw = this.split.el.getWidth();
34289             box.width -= sw;
34290             this.split.el.setLeft(box.x);
34291             this.split.el.setTop(box.y);
34292             this.split.el.setHeight(box.height);
34293             box.x += sw;
34294         }
34295         if(this.collapsed){
34296             this.updateBody(null, box.height);
34297         }
34298         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34299     }
34300 });
34301
34302 Roo.WestLayoutRegion = function(mgr, config){
34303     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34304     if(this.split){
34305         this.split.placement = Roo.SplitBar.LEFT;
34306         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34307         this.split.el.addClass("x-layout-split-h");
34308     }
34309     var size = config.initialSize || config.width;
34310     if(typeof size != "undefined"){
34311         this.el.setWidth(size);
34312     }
34313 };
34314 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34315     orientation: Roo.SplitBar.HORIZONTAL,
34316     getBox : function(){
34317         if(this.collapsed){
34318             return this.collapsedEl.getBox();
34319         }
34320         var box = this.el.getBox();
34321         if(this.split){
34322             box.width += this.split.el.getWidth();
34323         }
34324         return box;
34325     },
34326     
34327     updateBox : function(box){
34328         if(this.split && !this.collapsed){
34329             var sw = this.split.el.getWidth();
34330             box.width -= sw;
34331             this.split.el.setLeft(box.x+box.width);
34332             this.split.el.setTop(box.y);
34333             this.split.el.setHeight(box.height);
34334         }
34335         if(this.collapsed){
34336             this.updateBody(null, box.height);
34337         }
34338         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34339     }
34340 });
34341 /*
34342  * Based on:
34343  * Ext JS Library 1.1.1
34344  * Copyright(c) 2006-2007, Ext JS, LLC.
34345  *
34346  * Originally Released Under LGPL - original licence link has changed is not relivant.
34347  *
34348  * Fork - LGPL
34349  * <script type="text/javascript">
34350  */
34351  
34352  
34353 /*
34354  * Private internal class for reading and applying state
34355  */
34356 Roo.LayoutStateManager = function(layout){
34357      // default empty state
34358      this.state = {
34359         north: {},
34360         south: {},
34361         east: {},
34362         west: {}       
34363     };
34364 };
34365
34366 Roo.LayoutStateManager.prototype = {
34367     init : function(layout, provider){
34368         this.provider = provider;
34369         var state = provider.get(layout.id+"-layout-state");
34370         if(state){
34371             var wasUpdating = layout.isUpdating();
34372             if(!wasUpdating){
34373                 layout.beginUpdate();
34374             }
34375             for(var key in state){
34376                 if(typeof state[key] != "function"){
34377                     var rstate = state[key];
34378                     var r = layout.getRegion(key);
34379                     if(r && rstate){
34380                         if(rstate.size){
34381                             r.resizeTo(rstate.size);
34382                         }
34383                         if(rstate.collapsed == true){
34384                             r.collapse(true);
34385                         }else{
34386                             r.expand(null, true);
34387                         }
34388                     }
34389                 }
34390             }
34391             if(!wasUpdating){
34392                 layout.endUpdate();
34393             }
34394             this.state = state; 
34395         }
34396         this.layout = layout;
34397         layout.on("regionresized", this.onRegionResized, this);
34398         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34399         layout.on("regionexpanded", this.onRegionExpanded, this);
34400     },
34401     
34402     storeState : function(){
34403         this.provider.set(this.layout.id+"-layout-state", this.state);
34404     },
34405     
34406     onRegionResized : function(region, newSize){
34407         this.state[region.getPosition()].size = newSize;
34408         this.storeState();
34409     },
34410     
34411     onRegionCollapsed : function(region){
34412         this.state[region.getPosition()].collapsed = true;
34413         this.storeState();
34414     },
34415     
34416     onRegionExpanded : function(region){
34417         this.state[region.getPosition()].collapsed = false;
34418         this.storeState();
34419     }
34420 };/*
34421  * Based on:
34422  * Ext JS Library 1.1.1
34423  * Copyright(c) 2006-2007, Ext JS, LLC.
34424  *
34425  * Originally Released Under LGPL - original licence link has changed is not relivant.
34426  *
34427  * Fork - LGPL
34428  * <script type="text/javascript">
34429  */
34430 /**
34431  * @class Roo.ContentPanel
34432  * @extends Roo.util.Observable
34433  * A basic ContentPanel element.
34434  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34435  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34436  * @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
34437  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34438  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34439  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34440  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34441  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34442  * @cfg {String} title          The title for this panel
34443  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34444  * @cfg {String} url            Calls {@link #setUrl} with this value
34445  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34446  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34447  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34448  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34449
34450  * @constructor
34451  * Create a new ContentPanel.
34452  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34453  * @param {String/Object} config A string to set only the title or a config object
34454  * @param {String} content (optional) Set the HTML content for this panel
34455  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34456  */
34457 Roo.ContentPanel = function(el, config, content){
34458     
34459      
34460     /*
34461     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34462         config = el;
34463         el = Roo.id();
34464     }
34465     if (config && config.parentLayout) { 
34466         el = config.parentLayout.el.createChild(); 
34467     }
34468     */
34469     if(el.autoCreate){ // xtype is available if this is called from factory
34470         config = el;
34471         el = Roo.id();
34472     }
34473     this.el = Roo.get(el);
34474     if(!this.el && config && config.autoCreate){
34475         if(typeof config.autoCreate == "object"){
34476             if(!config.autoCreate.id){
34477                 config.autoCreate.id = config.id||el;
34478             }
34479             this.el = Roo.DomHelper.append(document.body,
34480                         config.autoCreate, true);
34481         }else{
34482             this.el = Roo.DomHelper.append(document.body,
34483                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34484         }
34485     }
34486     this.closable = false;
34487     this.loaded = false;
34488     this.active = false;
34489     if(typeof config == "string"){
34490         this.title = config;
34491     }else{
34492         Roo.apply(this, config);
34493     }
34494     
34495     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34496         this.wrapEl = this.el.wrap();
34497         this.toolbar.container = this.el.insertSibling(false, 'before');
34498         this.toolbar = new Roo.Toolbar(this.toolbar);
34499     }
34500     
34501     // xtype created footer. - not sure if will work as we normally have to render first..
34502     if (this.footer && !this.footer.el && this.footer.xtype) {
34503         if (!this.wrapEl) {
34504             this.wrapEl = this.el.wrap();
34505         }
34506     
34507         this.footer.container = this.wrapEl.createChild();
34508          
34509         this.footer = Roo.factory(this.footer, Roo);
34510         
34511     }
34512     
34513     if(this.resizeEl){
34514         this.resizeEl = Roo.get(this.resizeEl, true);
34515     }else{
34516         this.resizeEl = this.el;
34517     }
34518     // handle view.xtype
34519     
34520  
34521     
34522     
34523     this.addEvents({
34524         /**
34525          * @event activate
34526          * Fires when this panel is activated. 
34527          * @param {Roo.ContentPanel} this
34528          */
34529         "activate" : true,
34530         /**
34531          * @event deactivate
34532          * Fires when this panel is activated. 
34533          * @param {Roo.ContentPanel} this
34534          */
34535         "deactivate" : true,
34536
34537         /**
34538          * @event resize
34539          * Fires when this panel is resized if fitToFrame is true.
34540          * @param {Roo.ContentPanel} this
34541          * @param {Number} width The width after any component adjustments
34542          * @param {Number} height The height after any component adjustments
34543          */
34544         "resize" : true,
34545         
34546          /**
34547          * @event render
34548          * Fires when this tab is created
34549          * @param {Roo.ContentPanel} this
34550          */
34551         "render" : true
34552         
34553         
34554         
34555     });
34556     
34557
34558     
34559     
34560     if(this.autoScroll){
34561         this.resizeEl.setStyle("overflow", "auto");
34562     } else {
34563         // fix randome scrolling
34564         this.el.on('scroll', function() {
34565             Roo.log('fix random scolling');
34566             this.scrollTo('top',0); 
34567         });
34568     }
34569     content = content || this.content;
34570     if(content){
34571         this.setContent(content);
34572     }
34573     if(config && config.url){
34574         this.setUrl(this.url, this.params, this.loadOnce);
34575     }
34576     
34577     
34578     
34579     Roo.ContentPanel.superclass.constructor.call(this);
34580     
34581     if (this.view && typeof(this.view.xtype) != 'undefined') {
34582         this.view.el = this.el.appendChild(document.createElement("div"));
34583         this.view = Roo.factory(this.view); 
34584         this.view.render  &&  this.view.render(false, '');  
34585     }
34586     
34587     
34588     this.fireEvent('render', this);
34589 };
34590
34591 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34592     tabTip:'',
34593     setRegion : function(region){
34594         this.region = region;
34595         if(region){
34596            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34597         }else{
34598            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34599         } 
34600     },
34601     
34602     /**
34603      * Returns the toolbar for this Panel if one was configured. 
34604      * @return {Roo.Toolbar} 
34605      */
34606     getToolbar : function(){
34607         return this.toolbar;
34608     },
34609     
34610     setActiveState : function(active){
34611         this.active = active;
34612         if(!active){
34613             this.fireEvent("deactivate", this);
34614         }else{
34615             this.fireEvent("activate", this);
34616         }
34617     },
34618     /**
34619      * Updates this panel's element
34620      * @param {String} content The new content
34621      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34622     */
34623     setContent : function(content, loadScripts){
34624         this.el.update(content, loadScripts);
34625     },
34626
34627     ignoreResize : function(w, h){
34628         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34629             return true;
34630         }else{
34631             this.lastSize = {width: w, height: h};
34632             return false;
34633         }
34634     },
34635     /**
34636      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34637      * @return {Roo.UpdateManager} The UpdateManager
34638      */
34639     getUpdateManager : function(){
34640         return this.el.getUpdateManager();
34641     },
34642      /**
34643      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34644      * @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:
34645 <pre><code>
34646 panel.load({
34647     url: "your-url.php",
34648     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34649     callback: yourFunction,
34650     scope: yourObject, //(optional scope)
34651     discardUrl: false,
34652     nocache: false,
34653     text: "Loading...",
34654     timeout: 30,
34655     scripts: false
34656 });
34657 </code></pre>
34658      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34659      * 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.
34660      * @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}
34661      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34662      * @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.
34663      * @return {Roo.ContentPanel} this
34664      */
34665     load : function(){
34666         var um = this.el.getUpdateManager();
34667         um.update.apply(um, arguments);
34668         return this;
34669     },
34670
34671
34672     /**
34673      * 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.
34674      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34675      * @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)
34676      * @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)
34677      * @return {Roo.UpdateManager} The UpdateManager
34678      */
34679     setUrl : function(url, params, loadOnce){
34680         if(this.refreshDelegate){
34681             this.removeListener("activate", this.refreshDelegate);
34682         }
34683         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34684         this.on("activate", this.refreshDelegate);
34685         return this.el.getUpdateManager();
34686     },
34687     
34688     _handleRefresh : function(url, params, loadOnce){
34689         if(!loadOnce || !this.loaded){
34690             var updater = this.el.getUpdateManager();
34691             updater.update(url, params, this._setLoaded.createDelegate(this));
34692         }
34693     },
34694     
34695     _setLoaded : function(){
34696         this.loaded = true;
34697     }, 
34698     
34699     /**
34700      * Returns this panel's id
34701      * @return {String} 
34702      */
34703     getId : function(){
34704         return this.el.id;
34705     },
34706     
34707     /** 
34708      * Returns this panel's element - used by regiosn to add.
34709      * @return {Roo.Element} 
34710      */
34711     getEl : function(){
34712         return this.wrapEl || this.el;
34713     },
34714     
34715     adjustForComponents : function(width, height)
34716     {
34717         //Roo.log('adjustForComponents ');
34718         if(this.resizeEl != this.el){
34719             width -= this.el.getFrameWidth('lr');
34720             height -= this.el.getFrameWidth('tb');
34721         }
34722         if(this.toolbar){
34723             var te = this.toolbar.getEl();
34724             height -= te.getHeight();
34725             te.setWidth(width);
34726         }
34727         if(this.footer){
34728             var te = this.footer.getEl();
34729             Roo.log("footer:" + te.getHeight());
34730             
34731             height -= te.getHeight();
34732             te.setWidth(width);
34733         }
34734         
34735         
34736         if(this.adjustments){
34737             width += this.adjustments[0];
34738             height += this.adjustments[1];
34739         }
34740         return {"width": width, "height": height};
34741     },
34742     
34743     setSize : function(width, height){
34744         if(this.fitToFrame && !this.ignoreResize(width, height)){
34745             if(this.fitContainer && this.resizeEl != this.el){
34746                 this.el.setSize(width, height);
34747             }
34748             var size = this.adjustForComponents(width, height);
34749             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34750             this.fireEvent('resize', this, size.width, size.height);
34751         }
34752     },
34753     
34754     /**
34755      * Returns this panel's title
34756      * @return {String} 
34757      */
34758     getTitle : function(){
34759         return this.title;
34760     },
34761     
34762     /**
34763      * Set this panel's title
34764      * @param {String} title
34765      */
34766     setTitle : function(title){
34767         this.title = title;
34768         if(this.region){
34769             this.region.updatePanelTitle(this, title);
34770         }
34771     },
34772     
34773     /**
34774      * Returns true is this panel was configured to be closable
34775      * @return {Boolean} 
34776      */
34777     isClosable : function(){
34778         return this.closable;
34779     },
34780     
34781     beforeSlide : function(){
34782         this.el.clip();
34783         this.resizeEl.clip();
34784     },
34785     
34786     afterSlide : function(){
34787         this.el.unclip();
34788         this.resizeEl.unclip();
34789     },
34790     
34791     /**
34792      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34793      *   Will fail silently if the {@link #setUrl} method has not been called.
34794      *   This does not activate the panel, just updates its content.
34795      */
34796     refresh : function(){
34797         if(this.refreshDelegate){
34798            this.loaded = false;
34799            this.refreshDelegate();
34800         }
34801     },
34802     
34803     /**
34804      * Destroys this panel
34805      */
34806     destroy : function(){
34807         this.el.removeAllListeners();
34808         var tempEl = document.createElement("span");
34809         tempEl.appendChild(this.el.dom);
34810         tempEl.innerHTML = "";
34811         this.el.remove();
34812         this.el = null;
34813     },
34814     
34815     /**
34816      * form - if the content panel contains a form - this is a reference to it.
34817      * @type {Roo.form.Form}
34818      */
34819     form : false,
34820     /**
34821      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34822      *    This contains a reference to it.
34823      * @type {Roo.View}
34824      */
34825     view : false,
34826     
34827       /**
34828      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34829      * <pre><code>
34830
34831 layout.addxtype({
34832        xtype : 'Form',
34833        items: [ .... ]
34834    }
34835 );
34836
34837 </code></pre>
34838      * @param {Object} cfg Xtype definition of item to add.
34839      */
34840     
34841     addxtype : function(cfg) {
34842         // add form..
34843         if (cfg.xtype.match(/^Form$/)) {
34844             
34845             var el;
34846             //if (this.footer) {
34847             //    el = this.footer.container.insertSibling(false, 'before');
34848             //} else {
34849                 el = this.el.createChild();
34850             //}
34851
34852             this.form = new  Roo.form.Form(cfg);
34853             
34854             
34855             if ( this.form.allItems.length) this.form.render(el.dom);
34856             return this.form;
34857         }
34858         // should only have one of theses..
34859         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34860             // views.. should not be just added - used named prop 'view''
34861             
34862             cfg.el = this.el.appendChild(document.createElement("div"));
34863             // factory?
34864             
34865             var ret = new Roo.factory(cfg);
34866              
34867              ret.render && ret.render(false, ''); // render blank..
34868             this.view = ret;
34869             return ret;
34870         }
34871         return false;
34872     }
34873 });
34874
34875 /**
34876  * @class Roo.GridPanel
34877  * @extends Roo.ContentPanel
34878  * @constructor
34879  * Create a new GridPanel.
34880  * @param {Roo.grid.Grid} grid The grid for this panel
34881  * @param {String/Object} config A string to set only the panel's title, or a config object
34882  */
34883 Roo.GridPanel = function(grid, config){
34884     
34885   
34886     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34887         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34888         
34889     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34890     
34891     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34892     
34893     if(this.toolbar){
34894         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34895     }
34896     // xtype created footer. - not sure if will work as we normally have to render first..
34897     if (this.footer && !this.footer.el && this.footer.xtype) {
34898         
34899         this.footer.container = this.grid.getView().getFooterPanel(true);
34900         this.footer.dataSource = this.grid.dataSource;
34901         this.footer = Roo.factory(this.footer, Roo);
34902         
34903     }
34904     
34905     grid.monitorWindowResize = false; // turn off autosizing
34906     grid.autoHeight = false;
34907     grid.autoWidth = false;
34908     this.grid = grid;
34909     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34910 };
34911
34912 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34913     getId : function(){
34914         return this.grid.id;
34915     },
34916     
34917     /**
34918      * Returns the grid for this panel
34919      * @return {Roo.grid.Grid} 
34920      */
34921     getGrid : function(){
34922         return this.grid;    
34923     },
34924     
34925     setSize : function(width, height){
34926         if(!this.ignoreResize(width, height)){
34927             var grid = this.grid;
34928             var size = this.adjustForComponents(width, height);
34929             grid.getGridEl().setSize(size.width, size.height);
34930             grid.autoSize();
34931         }
34932     },
34933     
34934     beforeSlide : function(){
34935         this.grid.getView().scroller.clip();
34936     },
34937     
34938     afterSlide : function(){
34939         this.grid.getView().scroller.unclip();
34940     },
34941     
34942     destroy : function(){
34943         this.grid.destroy();
34944         delete this.grid;
34945         Roo.GridPanel.superclass.destroy.call(this); 
34946     }
34947 });
34948
34949
34950 /**
34951  * @class Roo.NestedLayoutPanel
34952  * @extends Roo.ContentPanel
34953  * @constructor
34954  * Create a new NestedLayoutPanel.
34955  * 
34956  * 
34957  * @param {Roo.BorderLayout} layout The layout for this panel
34958  * @param {String/Object} config A string to set only the title or a config object
34959  */
34960 Roo.NestedLayoutPanel = function(layout, config)
34961 {
34962     // construct with only one argument..
34963     /* FIXME - implement nicer consturctors
34964     if (layout.layout) {
34965         config = layout;
34966         layout = config.layout;
34967         delete config.layout;
34968     }
34969     if (layout.xtype && !layout.getEl) {
34970         // then layout needs constructing..
34971         layout = Roo.factory(layout, Roo);
34972     }
34973     */
34974     
34975     
34976     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
34977     
34978     layout.monitorWindowResize = false; // turn off autosizing
34979     this.layout = layout;
34980     this.layout.getEl().addClass("x-layout-nested-layout");
34981     
34982     
34983     
34984     
34985 };
34986
34987 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
34988
34989     setSize : function(width, height){
34990         if(!this.ignoreResize(width, height)){
34991             var size = this.adjustForComponents(width, height);
34992             var el = this.layout.getEl();
34993             el.setSize(size.width, size.height);
34994             var touch = el.dom.offsetWidth;
34995             this.layout.layout();
34996             // ie requires a double layout on the first pass
34997             if(Roo.isIE && !this.initialized){
34998                 this.initialized = true;
34999                 this.layout.layout();
35000             }
35001         }
35002     },
35003     
35004     // activate all subpanels if not currently active..
35005     
35006     setActiveState : function(active){
35007         this.active = active;
35008         if(!active){
35009             this.fireEvent("deactivate", this);
35010             return;
35011         }
35012         
35013         this.fireEvent("activate", this);
35014         // not sure if this should happen before or after..
35015         if (!this.layout) {
35016             return; // should not happen..
35017         }
35018         var reg = false;
35019         for (var r in this.layout.regions) {
35020             reg = this.layout.getRegion(r);
35021             if (reg.getActivePanel()) {
35022                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35023                 reg.setActivePanel(reg.getActivePanel());
35024                 continue;
35025             }
35026             if (!reg.panels.length) {
35027                 continue;
35028             }
35029             reg.showPanel(reg.getPanel(0));
35030         }
35031         
35032         
35033         
35034         
35035     },
35036     
35037     /**
35038      * Returns the nested BorderLayout for this panel
35039      * @return {Roo.BorderLayout} 
35040      */
35041     getLayout : function(){
35042         return this.layout;
35043     },
35044     
35045      /**
35046      * Adds a xtype elements to the layout of the nested panel
35047      * <pre><code>
35048
35049 panel.addxtype({
35050        xtype : 'ContentPanel',
35051        region: 'west',
35052        items: [ .... ]
35053    }
35054 );
35055
35056 panel.addxtype({
35057         xtype : 'NestedLayoutPanel',
35058         region: 'west',
35059         layout: {
35060            center: { },
35061            west: { }   
35062         },
35063         items : [ ... list of content panels or nested layout panels.. ]
35064    }
35065 );
35066 </code></pre>
35067      * @param {Object} cfg Xtype definition of item to add.
35068      */
35069     addxtype : function(cfg) {
35070         return this.layout.addxtype(cfg);
35071     
35072     }
35073 });
35074
35075 Roo.ScrollPanel = function(el, config, content){
35076     config = config || {};
35077     config.fitToFrame = true;
35078     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35079     
35080     this.el.dom.style.overflow = "hidden";
35081     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35082     this.el.removeClass("x-layout-inactive-content");
35083     this.el.on("mousewheel", this.onWheel, this);
35084
35085     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35086     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35087     up.unselectable(); down.unselectable();
35088     up.on("click", this.scrollUp, this);
35089     down.on("click", this.scrollDown, this);
35090     up.addClassOnOver("x-scroller-btn-over");
35091     down.addClassOnOver("x-scroller-btn-over");
35092     up.addClassOnClick("x-scroller-btn-click");
35093     down.addClassOnClick("x-scroller-btn-click");
35094     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35095
35096     this.resizeEl = this.el;
35097     this.el = wrap; this.up = up; this.down = down;
35098 };
35099
35100 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35101     increment : 100,
35102     wheelIncrement : 5,
35103     scrollUp : function(){
35104         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35105     },
35106
35107     scrollDown : function(){
35108         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35109     },
35110
35111     afterScroll : function(){
35112         var el = this.resizeEl;
35113         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35114         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35115         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35116     },
35117
35118     setSize : function(){
35119         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35120         this.afterScroll();
35121     },
35122
35123     onWheel : function(e){
35124         var d = e.getWheelDelta();
35125         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35126         this.afterScroll();
35127         e.stopEvent();
35128     },
35129
35130     setContent : function(content, loadScripts){
35131         this.resizeEl.update(content, loadScripts);
35132     }
35133
35134 });
35135
35136
35137
35138
35139
35140
35141
35142
35143
35144 /**
35145  * @class Roo.TreePanel
35146  * @extends Roo.ContentPanel
35147  * @constructor
35148  * Create a new TreePanel. - defaults to fit/scoll contents.
35149  * @param {String/Object} config A string to set only the panel's title, or a config object
35150  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35151  */
35152 Roo.TreePanel = function(config){
35153     var el = config.el;
35154     var tree = config.tree;
35155     delete config.tree; 
35156     delete config.el; // hopefull!
35157     
35158     // wrapper for IE7 strict & safari scroll issue
35159     
35160     var treeEl = el.createChild();
35161     config.resizeEl = treeEl;
35162     
35163     
35164     
35165     Roo.TreePanel.superclass.constructor.call(this, el, config);
35166  
35167  
35168     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35169     //console.log(tree);
35170     this.on('activate', function()
35171     {
35172         if (this.tree.rendered) {
35173             return;
35174         }
35175         //console.log('render tree');
35176         this.tree.render();
35177     });
35178     // this should not be needed.. - it's actually the 'el' that resizes?
35179     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35180     
35181     //this.on('resize',  function (cp, w, h) {
35182     //        this.tree.innerCt.setWidth(w);
35183     //        this.tree.innerCt.setHeight(h);
35184     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35185     //});
35186
35187         
35188     
35189 };
35190
35191 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35192     fitToFrame : true,
35193     autoScroll : true
35194 });
35195
35196
35197
35198
35199
35200
35201
35202
35203
35204
35205
35206 /*
35207  * Based on:
35208  * Ext JS Library 1.1.1
35209  * Copyright(c) 2006-2007, Ext JS, LLC.
35210  *
35211  * Originally Released Under LGPL - original licence link has changed is not relivant.
35212  *
35213  * Fork - LGPL
35214  * <script type="text/javascript">
35215  */
35216  
35217
35218 /**
35219  * @class Roo.ReaderLayout
35220  * @extends Roo.BorderLayout
35221  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35222  * center region containing two nested regions (a top one for a list view and one for item preview below),
35223  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35224  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35225  * expedites the setup of the overall layout and regions for this common application style.
35226  * Example:
35227  <pre><code>
35228 var reader = new Roo.ReaderLayout();
35229 var CP = Roo.ContentPanel;  // shortcut for adding
35230
35231 reader.beginUpdate();
35232 reader.add("north", new CP("north", "North"));
35233 reader.add("west", new CP("west", {title: "West"}));
35234 reader.add("east", new CP("east", {title: "East"}));
35235
35236 reader.regions.listView.add(new CP("listView", "List"));
35237 reader.regions.preview.add(new CP("preview", "Preview"));
35238 reader.endUpdate();
35239 </code></pre>
35240 * @constructor
35241 * Create a new ReaderLayout
35242 * @param {Object} config Configuration options
35243 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35244 * document.body if omitted)
35245 */
35246 Roo.ReaderLayout = function(config, renderTo){
35247     var c = config || {size:{}};
35248     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35249         north: c.north !== false ? Roo.apply({
35250             split:false,
35251             initialSize: 32,
35252             titlebar: false
35253         }, c.north) : false,
35254         west: c.west !== false ? Roo.apply({
35255             split:true,
35256             initialSize: 200,
35257             minSize: 175,
35258             maxSize: 400,
35259             titlebar: true,
35260             collapsible: true,
35261             animate: true,
35262             margins:{left:5,right:0,bottom:5,top:5},
35263             cmargins:{left:5,right:5,bottom:5,top:5}
35264         }, c.west) : false,
35265         east: c.east !== false ? Roo.apply({
35266             split:true,
35267             initialSize: 200,
35268             minSize: 175,
35269             maxSize: 400,
35270             titlebar: true,
35271             collapsible: true,
35272             animate: true,
35273             margins:{left:0,right:5,bottom:5,top:5},
35274             cmargins:{left:5,right:5,bottom:5,top:5}
35275         }, c.east) : false,
35276         center: Roo.apply({
35277             tabPosition: 'top',
35278             autoScroll:false,
35279             closeOnTab: true,
35280             titlebar:false,
35281             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35282         }, c.center)
35283     });
35284
35285     this.el.addClass('x-reader');
35286
35287     this.beginUpdate();
35288
35289     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35290         south: c.preview !== false ? Roo.apply({
35291             split:true,
35292             initialSize: 200,
35293             minSize: 100,
35294             autoScroll:true,
35295             collapsible:true,
35296             titlebar: true,
35297             cmargins:{top:5,left:0, right:0, bottom:0}
35298         }, c.preview) : false,
35299         center: Roo.apply({
35300             autoScroll:false,
35301             titlebar:false,
35302             minHeight:200
35303         }, c.listView)
35304     });
35305     this.add('center', new Roo.NestedLayoutPanel(inner,
35306             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35307
35308     this.endUpdate();
35309
35310     this.regions.preview = inner.getRegion('south');
35311     this.regions.listView = inner.getRegion('center');
35312 };
35313
35314 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35315  * Based on:
35316  * Ext JS Library 1.1.1
35317  * Copyright(c) 2006-2007, Ext JS, LLC.
35318  *
35319  * Originally Released Under LGPL - original licence link has changed is not relivant.
35320  *
35321  * Fork - LGPL
35322  * <script type="text/javascript">
35323  */
35324  
35325 /**
35326  * @class Roo.grid.Grid
35327  * @extends Roo.util.Observable
35328  * This class represents the primary interface of a component based grid control.
35329  * <br><br>Usage:<pre><code>
35330  var grid = new Roo.grid.Grid("my-container-id", {
35331      ds: myDataStore,
35332      cm: myColModel,
35333      selModel: mySelectionModel,
35334      autoSizeColumns: true,
35335      monitorWindowResize: false,
35336      trackMouseOver: true
35337  });
35338  // set any options
35339  grid.render();
35340  * </code></pre>
35341  * <b>Common Problems:</b><br/>
35342  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35343  * element will correct this<br/>
35344  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35345  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35346  * are unpredictable.<br/>
35347  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35348  * grid to calculate dimensions/offsets.<br/>
35349   * @constructor
35350  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35351  * The container MUST have some type of size defined for the grid to fill. The container will be
35352  * automatically set to position relative if it isn't already.
35353  * @param {Object} config A config object that sets properties on this grid.
35354  */
35355 Roo.grid.Grid = function(container, config){
35356         // initialize the container
35357         this.container = Roo.get(container);
35358         this.container.update("");
35359         this.container.setStyle("overflow", "hidden");
35360     this.container.addClass('x-grid-container');
35361
35362     this.id = this.container.id;
35363
35364     Roo.apply(this, config);
35365     // check and correct shorthanded configs
35366     if(this.ds){
35367         this.dataSource = this.ds;
35368         delete this.ds;
35369     }
35370     if(this.cm){
35371         this.colModel = this.cm;
35372         delete this.cm;
35373     }
35374     if(this.sm){
35375         this.selModel = this.sm;
35376         delete this.sm;
35377     }
35378
35379     if (this.selModel) {
35380         this.selModel = Roo.factory(this.selModel, Roo.grid);
35381         this.sm = this.selModel;
35382         this.sm.xmodule = this.xmodule || false;
35383     }
35384     if (typeof(this.colModel.config) == 'undefined') {
35385         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35386         this.cm = this.colModel;
35387         this.cm.xmodule = this.xmodule || false;
35388     }
35389     if (this.dataSource) {
35390         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35391         this.ds = this.dataSource;
35392         this.ds.xmodule = this.xmodule || false;
35393          
35394     }
35395     
35396     
35397     
35398     if(this.width){
35399         this.container.setWidth(this.width);
35400     }
35401
35402     if(this.height){
35403         this.container.setHeight(this.height);
35404     }
35405     /** @private */
35406         this.addEvents({
35407         // raw events
35408         /**
35409          * @event click
35410          * The raw click event for the entire grid.
35411          * @param {Roo.EventObject} e
35412          */
35413         "click" : true,
35414         /**
35415          * @event dblclick
35416          * The raw dblclick event for the entire grid.
35417          * @param {Roo.EventObject} e
35418          */
35419         "dblclick" : true,
35420         /**
35421          * @event contextmenu
35422          * The raw contextmenu event for the entire grid.
35423          * @param {Roo.EventObject} e
35424          */
35425         "contextmenu" : true,
35426         /**
35427          * @event mousedown
35428          * The raw mousedown event for the entire grid.
35429          * @param {Roo.EventObject} e
35430          */
35431         "mousedown" : true,
35432         /**
35433          * @event mouseup
35434          * The raw mouseup event for the entire grid.
35435          * @param {Roo.EventObject} e
35436          */
35437         "mouseup" : true,
35438         /**
35439          * @event mouseover
35440          * The raw mouseover event for the entire grid.
35441          * @param {Roo.EventObject} e
35442          */
35443         "mouseover" : true,
35444         /**
35445          * @event mouseout
35446          * The raw mouseout event for the entire grid.
35447          * @param {Roo.EventObject} e
35448          */
35449         "mouseout" : true,
35450         /**
35451          * @event keypress
35452          * The raw keypress event for the entire grid.
35453          * @param {Roo.EventObject} e
35454          */
35455         "keypress" : true,
35456         /**
35457          * @event keydown
35458          * The raw keydown event for the entire grid.
35459          * @param {Roo.EventObject} e
35460          */
35461         "keydown" : true,
35462
35463         // custom events
35464
35465         /**
35466          * @event cellclick
35467          * Fires when a cell is clicked
35468          * @param {Grid} this
35469          * @param {Number} rowIndex
35470          * @param {Number} columnIndex
35471          * @param {Roo.EventObject} e
35472          */
35473         "cellclick" : true,
35474         /**
35475          * @event celldblclick
35476          * Fires when a cell is double clicked
35477          * @param {Grid} this
35478          * @param {Number} rowIndex
35479          * @param {Number} columnIndex
35480          * @param {Roo.EventObject} e
35481          */
35482         "celldblclick" : true,
35483         /**
35484          * @event rowclick
35485          * Fires when a row is clicked
35486          * @param {Grid} this
35487          * @param {Number} rowIndex
35488          * @param {Roo.EventObject} e
35489          */
35490         "rowclick" : true,
35491         /**
35492          * @event rowdblclick
35493          * Fires when a row is double clicked
35494          * @param {Grid} this
35495          * @param {Number} rowIndex
35496          * @param {Roo.EventObject} e
35497          */
35498         "rowdblclick" : true,
35499         /**
35500          * @event headerclick
35501          * Fires when a header is clicked
35502          * @param {Grid} this
35503          * @param {Number} columnIndex
35504          * @param {Roo.EventObject} e
35505          */
35506         "headerclick" : true,
35507         /**
35508          * @event headerdblclick
35509          * Fires when a header cell is double clicked
35510          * @param {Grid} this
35511          * @param {Number} columnIndex
35512          * @param {Roo.EventObject} e
35513          */
35514         "headerdblclick" : true,
35515         /**
35516          * @event rowcontextmenu
35517          * Fires when a row is right clicked
35518          * @param {Grid} this
35519          * @param {Number} rowIndex
35520          * @param {Roo.EventObject} e
35521          */
35522         "rowcontextmenu" : true,
35523         /**
35524          * @event cellcontextmenu
35525          * Fires when a cell is right clicked
35526          * @param {Grid} this
35527          * @param {Number} rowIndex
35528          * @param {Number} cellIndex
35529          * @param {Roo.EventObject} e
35530          */
35531          "cellcontextmenu" : true,
35532         /**
35533          * @event headercontextmenu
35534          * Fires when a header is right clicked
35535          * @param {Grid} this
35536          * @param {Number} columnIndex
35537          * @param {Roo.EventObject} e
35538          */
35539         "headercontextmenu" : true,
35540         /**
35541          * @event bodyscroll
35542          * Fires when the body element is scrolled
35543          * @param {Number} scrollLeft
35544          * @param {Number} scrollTop
35545          */
35546         "bodyscroll" : true,
35547         /**
35548          * @event columnresize
35549          * Fires when the user resizes a column
35550          * @param {Number} columnIndex
35551          * @param {Number} newSize
35552          */
35553         "columnresize" : true,
35554         /**
35555          * @event columnmove
35556          * Fires when the user moves a column
35557          * @param {Number} oldIndex
35558          * @param {Number} newIndex
35559          */
35560         "columnmove" : true,
35561         /**
35562          * @event startdrag
35563          * Fires when row(s) start being dragged
35564          * @param {Grid} this
35565          * @param {Roo.GridDD} dd The drag drop object
35566          * @param {event} e The raw browser event
35567          */
35568         "startdrag" : true,
35569         /**
35570          * @event enddrag
35571          * Fires when a drag operation is complete
35572          * @param {Grid} this
35573          * @param {Roo.GridDD} dd The drag drop object
35574          * @param {event} e The raw browser event
35575          */
35576         "enddrag" : true,
35577         /**
35578          * @event dragdrop
35579          * Fires when dragged row(s) are dropped on a valid DD target
35580          * @param {Grid} this
35581          * @param {Roo.GridDD} dd The drag drop object
35582          * @param {String} targetId The target drag drop object
35583          * @param {event} e The raw browser event
35584          */
35585         "dragdrop" : true,
35586         /**
35587          * @event dragover
35588          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35589          * @param {Grid} this
35590          * @param {Roo.GridDD} dd The drag drop object
35591          * @param {String} targetId The target drag drop object
35592          * @param {event} e The raw browser event
35593          */
35594         "dragover" : true,
35595         /**
35596          * @event dragenter
35597          *  Fires when the dragged row(s) first cross another DD target while being dragged
35598          * @param {Grid} this
35599          * @param {Roo.GridDD} dd The drag drop object
35600          * @param {String} targetId The target drag drop object
35601          * @param {event} e The raw browser event
35602          */
35603         "dragenter" : true,
35604         /**
35605          * @event dragout
35606          * Fires when the dragged row(s) leave another DD target while being dragged
35607          * @param {Grid} this
35608          * @param {Roo.GridDD} dd The drag drop object
35609          * @param {String} targetId The target drag drop object
35610          * @param {event} e The raw browser event
35611          */
35612         "dragout" : true,
35613         /**
35614          * @event rowclass
35615          * Fires when a row is rendered, so you can change add a style to it.
35616          * @param {GridView} gridview   The grid view
35617          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35618          */
35619         'rowclass' : true,
35620
35621         /**
35622          * @event render
35623          * Fires when the grid is rendered
35624          * @param {Grid} grid
35625          */
35626         'render' : true
35627     });
35628
35629     Roo.grid.Grid.superclass.constructor.call(this);
35630 };
35631 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35632     
35633     /**
35634      * @cfg {String} ddGroup - drag drop group.
35635      */
35636
35637     /**
35638      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35639      */
35640     minColumnWidth : 25,
35641
35642     /**
35643      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35644      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35645      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35646      */
35647     autoSizeColumns : false,
35648
35649     /**
35650      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35651      */
35652     autoSizeHeaders : true,
35653
35654     /**
35655      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35656      */
35657     monitorWindowResize : true,
35658
35659     /**
35660      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35661      * rows measured to get a columns size. Default is 0 (all rows).
35662      */
35663     maxRowsToMeasure : 0,
35664
35665     /**
35666      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35667      */
35668     trackMouseOver : true,
35669
35670     /**
35671     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35672     */
35673     
35674     /**
35675     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35676     */
35677     enableDragDrop : false,
35678     
35679     /**
35680     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35681     */
35682     enableColumnMove : true,
35683     
35684     /**
35685     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35686     */
35687     enableColumnHide : true,
35688     
35689     /**
35690     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35691     */
35692     enableRowHeightSync : false,
35693     
35694     /**
35695     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35696     */
35697     stripeRows : true,
35698     
35699     /**
35700     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35701     */
35702     autoHeight : false,
35703
35704     /**
35705      * @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.
35706      */
35707     autoExpandColumn : false,
35708
35709     /**
35710     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35711     * Default is 50.
35712     */
35713     autoExpandMin : 50,
35714
35715     /**
35716     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35717     */
35718     autoExpandMax : 1000,
35719
35720     /**
35721     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35722     */
35723     view : null,
35724
35725     /**
35726     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35727     */
35728     loadMask : false,
35729     /**
35730     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35731     */
35732     dropTarget: false,
35733     
35734    
35735     
35736     // private
35737     rendered : false,
35738
35739     /**
35740     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35741     * of a fixed width. Default is false.
35742     */
35743     /**
35744     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35745     */
35746     /**
35747      * Called once after all setup has been completed and the grid is ready to be rendered.
35748      * @return {Roo.grid.Grid} this
35749      */
35750     render : function()
35751     {
35752         var c = this.container;
35753         // try to detect autoHeight/width mode
35754         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35755             this.autoHeight = true;
35756         }
35757         var view = this.getView();
35758         view.init(this);
35759
35760         c.on("click", this.onClick, this);
35761         c.on("dblclick", this.onDblClick, this);
35762         c.on("contextmenu", this.onContextMenu, this);
35763         c.on("keydown", this.onKeyDown, this);
35764         if (Roo.isTouch) {
35765             c.on("touchstart", this.onTouchStart, this);
35766         }
35767
35768         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35769
35770         this.getSelectionModel().init(this);
35771
35772         view.render();
35773
35774         if(this.loadMask){
35775             this.loadMask = new Roo.LoadMask(this.container,
35776                     Roo.apply({store:this.dataSource}, this.loadMask));
35777         }
35778         
35779         
35780         if (this.toolbar && this.toolbar.xtype) {
35781             this.toolbar.container = this.getView().getHeaderPanel(true);
35782             this.toolbar = new Roo.Toolbar(this.toolbar);
35783         }
35784         if (this.footer && this.footer.xtype) {
35785             this.footer.dataSource = this.getDataSource();
35786             this.footer.container = this.getView().getFooterPanel(true);
35787             this.footer = Roo.factory(this.footer, Roo);
35788         }
35789         if (this.dropTarget && this.dropTarget.xtype) {
35790             delete this.dropTarget.xtype;
35791             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35792         }
35793         
35794         
35795         this.rendered = true;
35796         this.fireEvent('render', this);
35797         return this;
35798     },
35799
35800         /**
35801          * Reconfigures the grid to use a different Store and Column Model.
35802          * The View will be bound to the new objects and refreshed.
35803          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35804          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35805          */
35806     reconfigure : function(dataSource, colModel){
35807         if(this.loadMask){
35808             this.loadMask.destroy();
35809             this.loadMask = new Roo.LoadMask(this.container,
35810                     Roo.apply({store:dataSource}, this.loadMask));
35811         }
35812         this.view.bind(dataSource, colModel);
35813         this.dataSource = dataSource;
35814         this.colModel = colModel;
35815         this.view.refresh(true);
35816     },
35817
35818     // private
35819     onKeyDown : function(e){
35820         this.fireEvent("keydown", e);
35821     },
35822
35823     /**
35824      * Destroy this grid.
35825      * @param {Boolean} removeEl True to remove the element
35826      */
35827     destroy : function(removeEl, keepListeners){
35828         if(this.loadMask){
35829             this.loadMask.destroy();
35830         }
35831         var c = this.container;
35832         c.removeAllListeners();
35833         this.view.destroy();
35834         this.colModel.purgeListeners();
35835         if(!keepListeners){
35836             this.purgeListeners();
35837         }
35838         c.update("");
35839         if(removeEl === true){
35840             c.remove();
35841         }
35842     },
35843
35844     // private
35845     processEvent : function(name, e){
35846         // does this fire select???
35847         Roo.log('grid:processEvent '  + name);
35848         
35849         if (name != 'touchstart' ) {
35850             this.fireEvent(name, e);    
35851         }
35852         
35853         var t = e.getTarget();
35854         var v = this.view;
35855         var header = v.findHeaderIndex(t);
35856         if(header !== false){
35857             var ename = name == 'touchstart' ? 'click' : name;
35858              
35859             this.fireEvent("header" + ename, this, header, e);
35860         }else{
35861             var row = v.findRowIndex(t);
35862             var cell = v.findCellIndex(t);
35863             if (name == 'touchstart') {
35864                 // first touch is always a click.
35865                 // hopefull this happens after selection is updated.?
35866                 name = false;
35867                 
35868                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35869                     var cs = this.selModel.getSelectedCell();
35870                     if (row == cs[0] && cell == cs[1]){
35871                         name = 'dblclick';
35872                     }
35873                 }
35874                 if (typeof(this.selModel.getSelections) != 'undefined') {
35875                     var cs = this.selModel.getSelections();
35876                     var ds = this.dataSource;
35877                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
35878                         name = 'dblclick';
35879                     }
35880                 }
35881                 if (!name) {
35882                     return;
35883                 }
35884             }
35885             
35886             
35887             if(row !== false){
35888                 this.fireEvent("row" + name, this, row, e);
35889                 if(cell !== false){
35890                     this.fireEvent("cell" + name, this, row, cell, e);
35891                 }
35892             }
35893         }
35894     },
35895
35896     // private
35897     onClick : function(e){
35898         this.processEvent("click", e);
35899     },
35900    // private
35901     onTouchStart : function(e){
35902         this.processEvent("touchstart", e);
35903     },
35904
35905     // private
35906     onContextMenu : function(e, t){
35907         this.processEvent("contextmenu", e);
35908     },
35909
35910     // private
35911     onDblClick : function(e){
35912         this.processEvent("dblclick", e);
35913     },
35914
35915     // private
35916     walkCells : function(row, col, step, fn, scope){
35917         var cm = this.colModel, clen = cm.getColumnCount();
35918         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35919         if(step < 0){
35920             if(col < 0){
35921                 row--;
35922                 first = false;
35923             }
35924             while(row >= 0){
35925                 if(!first){
35926                     col = clen-1;
35927                 }
35928                 first = false;
35929                 while(col >= 0){
35930                     if(fn.call(scope || this, row, col, cm) === true){
35931                         return [row, col];
35932                     }
35933                     col--;
35934                 }
35935                 row--;
35936             }
35937         } else {
35938             if(col >= clen){
35939                 row++;
35940                 first = false;
35941             }
35942             while(row < rlen){
35943                 if(!first){
35944                     col = 0;
35945                 }
35946                 first = false;
35947                 while(col < clen){
35948                     if(fn.call(scope || this, row, col, cm) === true){
35949                         return [row, col];
35950                     }
35951                     col++;
35952                 }
35953                 row++;
35954             }
35955         }
35956         return null;
35957     },
35958
35959     // private
35960     getSelections : function(){
35961         return this.selModel.getSelections();
35962     },
35963
35964     /**
35965      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
35966      * but if manual update is required this method will initiate it.
35967      */
35968     autoSize : function(){
35969         if(this.rendered){
35970             this.view.layout();
35971             if(this.view.adjustForScroll){
35972                 this.view.adjustForScroll();
35973             }
35974         }
35975     },
35976
35977     /**
35978      * Returns the grid's underlying element.
35979      * @return {Element} The element
35980      */
35981     getGridEl : function(){
35982         return this.container;
35983     },
35984
35985     // private for compatibility, overridden by editor grid
35986     stopEditing : function(){},
35987
35988     /**
35989      * Returns the grid's SelectionModel.
35990      * @return {SelectionModel}
35991      */
35992     getSelectionModel : function(){
35993         if(!this.selModel){
35994             this.selModel = new Roo.grid.RowSelectionModel();
35995         }
35996         return this.selModel;
35997     },
35998
35999     /**
36000      * Returns the grid's DataSource.
36001      * @return {DataSource}
36002      */
36003     getDataSource : function(){
36004         return this.dataSource;
36005     },
36006
36007     /**
36008      * Returns the grid's ColumnModel.
36009      * @return {ColumnModel}
36010      */
36011     getColumnModel : function(){
36012         return this.colModel;
36013     },
36014
36015     /**
36016      * Returns the grid's GridView object.
36017      * @return {GridView}
36018      */
36019     getView : function(){
36020         if(!this.view){
36021             this.view = new Roo.grid.GridView(this.viewConfig);
36022         }
36023         return this.view;
36024     },
36025     /**
36026      * Called to get grid's drag proxy text, by default returns this.ddText.
36027      * @return {String}
36028      */
36029     getDragDropText : function(){
36030         var count = this.selModel.getCount();
36031         return String.format(this.ddText, count, count == 1 ? '' : 's');
36032     }
36033 });
36034 /**
36035  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36036  * %0 is replaced with the number of selected rows.
36037  * @type String
36038  */
36039 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36040  * Based on:
36041  * Ext JS Library 1.1.1
36042  * Copyright(c) 2006-2007, Ext JS, LLC.
36043  *
36044  * Originally Released Under LGPL - original licence link has changed is not relivant.
36045  *
36046  * Fork - LGPL
36047  * <script type="text/javascript">
36048  */
36049  
36050 Roo.grid.AbstractGridView = function(){
36051         this.grid = null;
36052         
36053         this.events = {
36054             "beforerowremoved" : true,
36055             "beforerowsinserted" : true,
36056             "beforerefresh" : true,
36057             "rowremoved" : true,
36058             "rowsinserted" : true,
36059             "rowupdated" : true,
36060             "refresh" : true
36061         };
36062     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36063 };
36064
36065 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36066     rowClass : "x-grid-row",
36067     cellClass : "x-grid-cell",
36068     tdClass : "x-grid-td",
36069     hdClass : "x-grid-hd",
36070     splitClass : "x-grid-hd-split",
36071     
36072     init: function(grid){
36073         this.grid = grid;
36074                 var cid = this.grid.getGridEl().id;
36075         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36076         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36077         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36078         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36079         },
36080         
36081     getColumnRenderers : function(){
36082         var renderers = [];
36083         var cm = this.grid.colModel;
36084         var colCount = cm.getColumnCount();
36085         for(var i = 0; i < colCount; i++){
36086             renderers[i] = cm.getRenderer(i);
36087         }
36088         return renderers;
36089     },
36090     
36091     getColumnIds : function(){
36092         var ids = [];
36093         var cm = this.grid.colModel;
36094         var colCount = cm.getColumnCount();
36095         for(var i = 0; i < colCount; i++){
36096             ids[i] = cm.getColumnId(i);
36097         }
36098         return ids;
36099     },
36100     
36101     getDataIndexes : function(){
36102         if(!this.indexMap){
36103             this.indexMap = this.buildIndexMap();
36104         }
36105         return this.indexMap.colToData;
36106     },
36107     
36108     getColumnIndexByDataIndex : function(dataIndex){
36109         if(!this.indexMap){
36110             this.indexMap = this.buildIndexMap();
36111         }
36112         return this.indexMap.dataToCol[dataIndex];
36113     },
36114     
36115     /**
36116      * Set a css style for a column dynamically. 
36117      * @param {Number} colIndex The index of the column
36118      * @param {String} name The css property name
36119      * @param {String} value The css value
36120      */
36121     setCSSStyle : function(colIndex, name, value){
36122         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36123         Roo.util.CSS.updateRule(selector, name, value);
36124     },
36125     
36126     generateRules : function(cm){
36127         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36128         Roo.util.CSS.removeStyleSheet(rulesId);
36129         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36130             var cid = cm.getColumnId(i);
36131             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36132                          this.tdSelector, cid, " {\n}\n",
36133                          this.hdSelector, cid, " {\n}\n",
36134                          this.splitSelector, cid, " {\n}\n");
36135         }
36136         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36137     }
36138 });/*
36139  * Based on:
36140  * Ext JS Library 1.1.1
36141  * Copyright(c) 2006-2007, Ext JS, LLC.
36142  *
36143  * Originally Released Under LGPL - original licence link has changed is not relivant.
36144  *
36145  * Fork - LGPL
36146  * <script type="text/javascript">
36147  */
36148
36149 // private
36150 // This is a support class used internally by the Grid components
36151 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36152     this.grid = grid;
36153     this.view = grid.getView();
36154     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36155     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36156     if(hd2){
36157         this.setHandleElId(Roo.id(hd));
36158         this.setOuterHandleElId(Roo.id(hd2));
36159     }
36160     this.scroll = false;
36161 };
36162 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36163     maxDragWidth: 120,
36164     getDragData : function(e){
36165         var t = Roo.lib.Event.getTarget(e);
36166         var h = this.view.findHeaderCell(t);
36167         if(h){
36168             return {ddel: h.firstChild, header:h};
36169         }
36170         return false;
36171     },
36172
36173     onInitDrag : function(e){
36174         this.view.headersDisabled = true;
36175         var clone = this.dragData.ddel.cloneNode(true);
36176         clone.id = Roo.id();
36177         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36178         this.proxy.update(clone);
36179         return true;
36180     },
36181
36182     afterValidDrop : function(){
36183         var v = this.view;
36184         setTimeout(function(){
36185             v.headersDisabled = false;
36186         }, 50);
36187     },
36188
36189     afterInvalidDrop : function(){
36190         var v = this.view;
36191         setTimeout(function(){
36192             v.headersDisabled = false;
36193         }, 50);
36194     }
36195 });
36196 /*
36197  * Based on:
36198  * Ext JS Library 1.1.1
36199  * Copyright(c) 2006-2007, Ext JS, LLC.
36200  *
36201  * Originally Released Under LGPL - original licence link has changed is not relivant.
36202  *
36203  * Fork - LGPL
36204  * <script type="text/javascript">
36205  */
36206 // private
36207 // This is a support class used internally by the Grid components
36208 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36209     this.grid = grid;
36210     this.view = grid.getView();
36211     // split the proxies so they don't interfere with mouse events
36212     this.proxyTop = Roo.DomHelper.append(document.body, {
36213         cls:"col-move-top", html:"&#160;"
36214     }, true);
36215     this.proxyBottom = Roo.DomHelper.append(document.body, {
36216         cls:"col-move-bottom", html:"&#160;"
36217     }, true);
36218     this.proxyTop.hide = this.proxyBottom.hide = function(){
36219         this.setLeftTop(-100,-100);
36220         this.setStyle("visibility", "hidden");
36221     };
36222     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36223     // temporarily disabled
36224     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36225     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36226 };
36227 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36228     proxyOffsets : [-4, -9],
36229     fly: Roo.Element.fly,
36230
36231     getTargetFromEvent : function(e){
36232         var t = Roo.lib.Event.getTarget(e);
36233         var cindex = this.view.findCellIndex(t);
36234         if(cindex !== false){
36235             return this.view.getHeaderCell(cindex);
36236         }
36237         return null;
36238     },
36239
36240     nextVisible : function(h){
36241         var v = this.view, cm = this.grid.colModel;
36242         h = h.nextSibling;
36243         while(h){
36244             if(!cm.isHidden(v.getCellIndex(h))){
36245                 return h;
36246             }
36247             h = h.nextSibling;
36248         }
36249         return null;
36250     },
36251
36252     prevVisible : function(h){
36253         var v = this.view, cm = this.grid.colModel;
36254         h = h.prevSibling;
36255         while(h){
36256             if(!cm.isHidden(v.getCellIndex(h))){
36257                 return h;
36258             }
36259             h = h.prevSibling;
36260         }
36261         return null;
36262     },
36263
36264     positionIndicator : function(h, n, e){
36265         var x = Roo.lib.Event.getPageX(e);
36266         var r = Roo.lib.Dom.getRegion(n.firstChild);
36267         var px, pt, py = r.top + this.proxyOffsets[1];
36268         if((r.right - x) <= (r.right-r.left)/2){
36269             px = r.right+this.view.borderWidth;
36270             pt = "after";
36271         }else{
36272             px = r.left;
36273             pt = "before";
36274         }
36275         var oldIndex = this.view.getCellIndex(h);
36276         var newIndex = this.view.getCellIndex(n);
36277
36278         if(this.grid.colModel.isFixed(newIndex)){
36279             return false;
36280         }
36281
36282         var locked = this.grid.colModel.isLocked(newIndex);
36283
36284         if(pt == "after"){
36285             newIndex++;
36286         }
36287         if(oldIndex < newIndex){
36288             newIndex--;
36289         }
36290         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36291             return false;
36292         }
36293         px +=  this.proxyOffsets[0];
36294         this.proxyTop.setLeftTop(px, py);
36295         this.proxyTop.show();
36296         if(!this.bottomOffset){
36297             this.bottomOffset = this.view.mainHd.getHeight();
36298         }
36299         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36300         this.proxyBottom.show();
36301         return pt;
36302     },
36303
36304     onNodeEnter : function(n, dd, e, data){
36305         if(data.header != n){
36306             this.positionIndicator(data.header, n, e);
36307         }
36308     },
36309
36310     onNodeOver : function(n, dd, e, data){
36311         var result = false;
36312         if(data.header != n){
36313             result = this.positionIndicator(data.header, n, e);
36314         }
36315         if(!result){
36316             this.proxyTop.hide();
36317             this.proxyBottom.hide();
36318         }
36319         return result ? this.dropAllowed : this.dropNotAllowed;
36320     },
36321
36322     onNodeOut : function(n, dd, e, data){
36323         this.proxyTop.hide();
36324         this.proxyBottom.hide();
36325     },
36326
36327     onNodeDrop : function(n, dd, e, data){
36328         var h = data.header;
36329         if(h != n){
36330             var cm = this.grid.colModel;
36331             var x = Roo.lib.Event.getPageX(e);
36332             var r = Roo.lib.Dom.getRegion(n.firstChild);
36333             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36334             var oldIndex = this.view.getCellIndex(h);
36335             var newIndex = this.view.getCellIndex(n);
36336             var locked = cm.isLocked(newIndex);
36337             if(pt == "after"){
36338                 newIndex++;
36339             }
36340             if(oldIndex < newIndex){
36341                 newIndex--;
36342             }
36343             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36344                 return false;
36345             }
36346             cm.setLocked(oldIndex, locked, true);
36347             cm.moveColumn(oldIndex, newIndex);
36348             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36349             return true;
36350         }
36351         return false;
36352     }
36353 });
36354 /*
36355  * Based on:
36356  * Ext JS Library 1.1.1
36357  * Copyright(c) 2006-2007, Ext JS, LLC.
36358  *
36359  * Originally Released Under LGPL - original licence link has changed is not relivant.
36360  *
36361  * Fork - LGPL
36362  * <script type="text/javascript">
36363  */
36364   
36365 /**
36366  * @class Roo.grid.GridView
36367  * @extends Roo.util.Observable
36368  *
36369  * @constructor
36370  * @param {Object} config
36371  */
36372 Roo.grid.GridView = function(config){
36373     Roo.grid.GridView.superclass.constructor.call(this);
36374     this.el = null;
36375
36376     Roo.apply(this, config);
36377 };
36378
36379 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36380
36381     unselectable :  'unselectable="on"',
36382     unselectableCls :  'x-unselectable',
36383     
36384     
36385     rowClass : "x-grid-row",
36386
36387     cellClass : "x-grid-col",
36388
36389     tdClass : "x-grid-td",
36390
36391     hdClass : "x-grid-hd",
36392
36393     splitClass : "x-grid-split",
36394
36395     sortClasses : ["sort-asc", "sort-desc"],
36396
36397     enableMoveAnim : false,
36398
36399     hlColor: "C3DAF9",
36400
36401     dh : Roo.DomHelper,
36402
36403     fly : Roo.Element.fly,
36404
36405     css : Roo.util.CSS,
36406
36407     borderWidth: 1,
36408
36409     splitOffset: 3,
36410
36411     scrollIncrement : 22,
36412
36413     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36414
36415     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36416
36417     bind : function(ds, cm){
36418         if(this.ds){
36419             this.ds.un("load", this.onLoad, this);
36420             this.ds.un("datachanged", this.onDataChange, this);
36421             this.ds.un("add", this.onAdd, this);
36422             this.ds.un("remove", this.onRemove, this);
36423             this.ds.un("update", this.onUpdate, this);
36424             this.ds.un("clear", this.onClear, this);
36425         }
36426         if(ds){
36427             ds.on("load", this.onLoad, this);
36428             ds.on("datachanged", this.onDataChange, this);
36429             ds.on("add", this.onAdd, this);
36430             ds.on("remove", this.onRemove, this);
36431             ds.on("update", this.onUpdate, this);
36432             ds.on("clear", this.onClear, this);
36433         }
36434         this.ds = ds;
36435
36436         if(this.cm){
36437             this.cm.un("widthchange", this.onColWidthChange, this);
36438             this.cm.un("headerchange", this.onHeaderChange, this);
36439             this.cm.un("hiddenchange", this.onHiddenChange, this);
36440             this.cm.un("columnmoved", this.onColumnMove, this);
36441             this.cm.un("columnlockchange", this.onColumnLock, this);
36442         }
36443         if(cm){
36444             this.generateRules(cm);
36445             cm.on("widthchange", this.onColWidthChange, this);
36446             cm.on("headerchange", this.onHeaderChange, this);
36447             cm.on("hiddenchange", this.onHiddenChange, this);
36448             cm.on("columnmoved", this.onColumnMove, this);
36449             cm.on("columnlockchange", this.onColumnLock, this);
36450         }
36451         this.cm = cm;
36452     },
36453
36454     init: function(grid){
36455         Roo.grid.GridView.superclass.init.call(this, grid);
36456
36457         this.bind(grid.dataSource, grid.colModel);
36458
36459         grid.on("headerclick", this.handleHeaderClick, this);
36460
36461         if(grid.trackMouseOver){
36462             grid.on("mouseover", this.onRowOver, this);
36463             grid.on("mouseout", this.onRowOut, this);
36464         }
36465         grid.cancelTextSelection = function(){};
36466         this.gridId = grid.id;
36467
36468         var tpls = this.templates || {};
36469
36470         if(!tpls.master){
36471             tpls.master = new Roo.Template(
36472                '<div class="x-grid" hidefocus="true">',
36473                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36474                   '<div class="x-grid-topbar"></div>',
36475                   '<div class="x-grid-scroller"><div></div></div>',
36476                   '<div class="x-grid-locked">',
36477                       '<div class="x-grid-header">{lockedHeader}</div>',
36478                       '<div class="x-grid-body">{lockedBody}</div>',
36479                   "</div>",
36480                   '<div class="x-grid-viewport">',
36481                       '<div class="x-grid-header">{header}</div>',
36482                       '<div class="x-grid-body">{body}</div>',
36483                   "</div>",
36484                   '<div class="x-grid-bottombar"></div>',
36485                  
36486                   '<div class="x-grid-resize-proxy">&#160;</div>',
36487                "</div>"
36488             );
36489             tpls.master.disableformats = true;
36490         }
36491
36492         if(!tpls.header){
36493             tpls.header = new Roo.Template(
36494                '<table border="0" cellspacing="0" cellpadding="0">',
36495                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36496                "</table>{splits}"
36497             );
36498             tpls.header.disableformats = true;
36499         }
36500         tpls.header.compile();
36501
36502         if(!tpls.hcell){
36503             tpls.hcell = new Roo.Template(
36504                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36505                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36506                 "</div></td>"
36507              );
36508              tpls.hcell.disableFormats = true;
36509         }
36510         tpls.hcell.compile();
36511
36512         if(!tpls.hsplit){
36513             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36514                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36515             tpls.hsplit.disableFormats = true;
36516         }
36517         tpls.hsplit.compile();
36518
36519         if(!tpls.body){
36520             tpls.body = new Roo.Template(
36521                '<table border="0" cellspacing="0" cellpadding="0">',
36522                "<tbody>{rows}</tbody>",
36523                "</table>"
36524             );
36525             tpls.body.disableFormats = true;
36526         }
36527         tpls.body.compile();
36528
36529         if(!tpls.row){
36530             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36531             tpls.row.disableFormats = true;
36532         }
36533         tpls.row.compile();
36534
36535         if(!tpls.cell){
36536             tpls.cell = new Roo.Template(
36537                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36538                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36539                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36540                 "</td>"
36541             );
36542             tpls.cell.disableFormats = true;
36543         }
36544         tpls.cell.compile();
36545
36546         this.templates = tpls;
36547     },
36548
36549     // remap these for backwards compat
36550     onColWidthChange : function(){
36551         this.updateColumns.apply(this, arguments);
36552     },
36553     onHeaderChange : function(){
36554         this.updateHeaders.apply(this, arguments);
36555     }, 
36556     onHiddenChange : function(){
36557         this.handleHiddenChange.apply(this, arguments);
36558     },
36559     onColumnMove : function(){
36560         this.handleColumnMove.apply(this, arguments);
36561     },
36562     onColumnLock : function(){
36563         this.handleLockChange.apply(this, arguments);
36564     },
36565
36566     onDataChange : function(){
36567         this.refresh();
36568         this.updateHeaderSortState();
36569     },
36570
36571     onClear : function(){
36572         this.refresh();
36573     },
36574
36575     onUpdate : function(ds, record){
36576         this.refreshRow(record);
36577     },
36578
36579     refreshRow : function(record){
36580         var ds = this.ds, index;
36581         if(typeof record == 'number'){
36582             index = record;
36583             record = ds.getAt(index);
36584         }else{
36585             index = ds.indexOf(record);
36586         }
36587         this.insertRows(ds, index, index, true);
36588         this.onRemove(ds, record, index+1, true);
36589         this.syncRowHeights(index, index);
36590         this.layout();
36591         this.fireEvent("rowupdated", this, index, record);
36592     },
36593
36594     onAdd : function(ds, records, index){
36595         this.insertRows(ds, index, index + (records.length-1));
36596     },
36597
36598     onRemove : function(ds, record, index, isUpdate){
36599         if(isUpdate !== true){
36600             this.fireEvent("beforerowremoved", this, index, record);
36601         }
36602         var bt = this.getBodyTable(), lt = this.getLockedTable();
36603         if(bt.rows[index]){
36604             bt.firstChild.removeChild(bt.rows[index]);
36605         }
36606         if(lt.rows[index]){
36607             lt.firstChild.removeChild(lt.rows[index]);
36608         }
36609         if(isUpdate !== true){
36610             this.stripeRows(index);
36611             this.syncRowHeights(index, index);
36612             this.layout();
36613             this.fireEvent("rowremoved", this, index, record);
36614         }
36615     },
36616
36617     onLoad : function(){
36618         this.scrollToTop();
36619     },
36620
36621     /**
36622      * Scrolls the grid to the top
36623      */
36624     scrollToTop : function(){
36625         if(this.scroller){
36626             this.scroller.dom.scrollTop = 0;
36627             this.syncScroll();
36628         }
36629     },
36630
36631     /**
36632      * Gets a panel in the header of the grid that can be used for toolbars etc.
36633      * After modifying the contents of this panel a call to grid.autoSize() may be
36634      * required to register any changes in size.
36635      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36636      * @return Roo.Element
36637      */
36638     getHeaderPanel : function(doShow){
36639         if(doShow){
36640             this.headerPanel.show();
36641         }
36642         return this.headerPanel;
36643     },
36644
36645     /**
36646      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36647      * After modifying the contents of this panel a call to grid.autoSize() may be
36648      * required to register any changes in size.
36649      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36650      * @return Roo.Element
36651      */
36652     getFooterPanel : function(doShow){
36653         if(doShow){
36654             this.footerPanel.show();
36655         }
36656         return this.footerPanel;
36657     },
36658
36659     initElements : function(){
36660         var E = Roo.Element;
36661         var el = this.grid.getGridEl().dom.firstChild;
36662         var cs = el.childNodes;
36663
36664         this.el = new E(el);
36665         
36666          this.focusEl = new E(el.firstChild);
36667         this.focusEl.swallowEvent("click", true);
36668         
36669         this.headerPanel = new E(cs[1]);
36670         this.headerPanel.enableDisplayMode("block");
36671
36672         this.scroller = new E(cs[2]);
36673         this.scrollSizer = new E(this.scroller.dom.firstChild);
36674
36675         this.lockedWrap = new E(cs[3]);
36676         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36677         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36678
36679         this.mainWrap = new E(cs[4]);
36680         this.mainHd = new E(this.mainWrap.dom.firstChild);
36681         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36682
36683         this.footerPanel = new E(cs[5]);
36684         this.footerPanel.enableDisplayMode("block");
36685
36686         this.resizeProxy = new E(cs[6]);
36687
36688         this.headerSelector = String.format(
36689            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36690            this.lockedHd.id, this.mainHd.id
36691         );
36692
36693         this.splitterSelector = String.format(
36694            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36695            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36696         );
36697     },
36698     idToCssName : function(s)
36699     {
36700         return s.replace(/[^a-z0-9]+/ig, '-');
36701     },
36702
36703     getHeaderCell : function(index){
36704         return Roo.DomQuery.select(this.headerSelector)[index];
36705     },
36706
36707     getHeaderCellMeasure : function(index){
36708         return this.getHeaderCell(index).firstChild;
36709     },
36710
36711     getHeaderCellText : function(index){
36712         return this.getHeaderCell(index).firstChild.firstChild;
36713     },
36714
36715     getLockedTable : function(){
36716         return this.lockedBody.dom.firstChild;
36717     },
36718
36719     getBodyTable : function(){
36720         return this.mainBody.dom.firstChild;
36721     },
36722
36723     getLockedRow : function(index){
36724         return this.getLockedTable().rows[index];
36725     },
36726
36727     getRow : function(index){
36728         return this.getBodyTable().rows[index];
36729     },
36730
36731     getRowComposite : function(index){
36732         if(!this.rowEl){
36733             this.rowEl = new Roo.CompositeElementLite();
36734         }
36735         var els = [], lrow, mrow;
36736         if(lrow = this.getLockedRow(index)){
36737             els.push(lrow);
36738         }
36739         if(mrow = this.getRow(index)){
36740             els.push(mrow);
36741         }
36742         this.rowEl.elements = els;
36743         return this.rowEl;
36744     },
36745     /**
36746      * Gets the 'td' of the cell
36747      * 
36748      * @param {Integer} rowIndex row to select
36749      * @param {Integer} colIndex column to select
36750      * 
36751      * @return {Object} 
36752      */
36753     getCell : function(rowIndex, colIndex){
36754         var locked = this.cm.getLockedCount();
36755         var source;
36756         if(colIndex < locked){
36757             source = this.lockedBody.dom.firstChild;
36758         }else{
36759             source = this.mainBody.dom.firstChild;
36760             colIndex -= locked;
36761         }
36762         return source.rows[rowIndex].childNodes[colIndex];
36763     },
36764
36765     getCellText : function(rowIndex, colIndex){
36766         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36767     },
36768
36769     getCellBox : function(cell){
36770         var b = this.fly(cell).getBox();
36771         if(Roo.isOpera){ // opera fails to report the Y
36772             b.y = cell.offsetTop + this.mainBody.getY();
36773         }
36774         return b;
36775     },
36776
36777     getCellIndex : function(cell){
36778         var id = String(cell.className).match(this.cellRE);
36779         if(id){
36780             return parseInt(id[1], 10);
36781         }
36782         return 0;
36783     },
36784
36785     findHeaderIndex : function(n){
36786         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36787         return r ? this.getCellIndex(r) : false;
36788     },
36789
36790     findHeaderCell : function(n){
36791         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36792         return r ? r : false;
36793     },
36794
36795     findRowIndex : function(n){
36796         if(!n){
36797             return false;
36798         }
36799         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36800         return r ? r.rowIndex : false;
36801     },
36802
36803     findCellIndex : function(node){
36804         var stop = this.el.dom;
36805         while(node && node != stop){
36806             if(this.findRE.test(node.className)){
36807                 return this.getCellIndex(node);
36808             }
36809             node = node.parentNode;
36810         }
36811         return false;
36812     },
36813
36814     getColumnId : function(index){
36815         return this.cm.getColumnId(index);
36816     },
36817
36818     getSplitters : function()
36819     {
36820         if(this.splitterSelector){
36821            return Roo.DomQuery.select(this.splitterSelector);
36822         }else{
36823             return null;
36824       }
36825     },
36826
36827     getSplitter : function(index){
36828         return this.getSplitters()[index];
36829     },
36830
36831     onRowOver : function(e, t){
36832         var row;
36833         if((row = this.findRowIndex(t)) !== false){
36834             this.getRowComposite(row).addClass("x-grid-row-over");
36835         }
36836     },
36837
36838     onRowOut : function(e, t){
36839         var row;
36840         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36841             this.getRowComposite(row).removeClass("x-grid-row-over");
36842         }
36843     },
36844
36845     renderHeaders : function(){
36846         var cm = this.cm;
36847         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36848         var cb = [], lb = [], sb = [], lsb = [], p = {};
36849         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36850             p.cellId = "x-grid-hd-0-" + i;
36851             p.splitId = "x-grid-csplit-0-" + i;
36852             p.id = cm.getColumnId(i);
36853             p.title = cm.getColumnTooltip(i) || "";
36854             p.value = cm.getColumnHeader(i) || "";
36855             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36856             if(!cm.isLocked(i)){
36857                 cb[cb.length] = ct.apply(p);
36858                 sb[sb.length] = st.apply(p);
36859             }else{
36860                 lb[lb.length] = ct.apply(p);
36861                 lsb[lsb.length] = st.apply(p);
36862             }
36863         }
36864         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36865                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36866     },
36867
36868     updateHeaders : function(){
36869         var html = this.renderHeaders();
36870         this.lockedHd.update(html[0]);
36871         this.mainHd.update(html[1]);
36872     },
36873
36874     /**
36875      * Focuses the specified row.
36876      * @param {Number} row The row index
36877      */
36878     focusRow : function(row)
36879     {
36880         //Roo.log('GridView.focusRow');
36881         var x = this.scroller.dom.scrollLeft;
36882         this.focusCell(row, 0, false);
36883         this.scroller.dom.scrollLeft = x;
36884     },
36885
36886     /**
36887      * Focuses the specified cell.
36888      * @param {Number} row The row index
36889      * @param {Number} col The column index
36890      * @param {Boolean} hscroll false to disable horizontal scrolling
36891      */
36892     focusCell : function(row, col, hscroll)
36893     {
36894         //Roo.log('GridView.focusCell');
36895         var el = this.ensureVisible(row, col, hscroll);
36896         this.focusEl.alignTo(el, "tl-tl");
36897         if(Roo.isGecko){
36898             this.focusEl.focus();
36899         }else{
36900             this.focusEl.focus.defer(1, this.focusEl);
36901         }
36902     },
36903
36904     /**
36905      * Scrolls the specified cell into view
36906      * @param {Number} row The row index
36907      * @param {Number} col The column index
36908      * @param {Boolean} hscroll false to disable horizontal scrolling
36909      */
36910     ensureVisible : function(row, col, hscroll)
36911     {
36912         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36913         //return null; //disable for testing.
36914         if(typeof row != "number"){
36915             row = row.rowIndex;
36916         }
36917         if(row < 0 && row >= this.ds.getCount()){
36918             return  null;
36919         }
36920         col = (col !== undefined ? col : 0);
36921         var cm = this.grid.colModel;
36922         while(cm.isHidden(col)){
36923             col++;
36924         }
36925
36926         var el = this.getCell(row, col);
36927         if(!el){
36928             return null;
36929         }
36930         var c = this.scroller.dom;
36931
36932         var ctop = parseInt(el.offsetTop, 10);
36933         var cleft = parseInt(el.offsetLeft, 10);
36934         var cbot = ctop + el.offsetHeight;
36935         var cright = cleft + el.offsetWidth;
36936         
36937         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36938         var stop = parseInt(c.scrollTop, 10);
36939         var sleft = parseInt(c.scrollLeft, 10);
36940         var sbot = stop + ch;
36941         var sright = sleft + c.clientWidth;
36942         /*
36943         Roo.log('GridView.ensureVisible:' +
36944                 ' ctop:' + ctop +
36945                 ' c.clientHeight:' + c.clientHeight +
36946                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36947                 ' stop:' + stop +
36948                 ' cbot:' + cbot +
36949                 ' sbot:' + sbot +
36950                 ' ch:' + ch  
36951                 );
36952         */
36953         if(ctop < stop){
36954              c.scrollTop = ctop;
36955             //Roo.log("set scrolltop to ctop DISABLE?");
36956         }else if(cbot > sbot){
36957             //Roo.log("set scrolltop to cbot-ch");
36958             c.scrollTop = cbot-ch;
36959         }
36960         
36961         if(hscroll !== false){
36962             if(cleft < sleft){
36963                 c.scrollLeft = cleft;
36964             }else if(cright > sright){
36965                 c.scrollLeft = cright-c.clientWidth;
36966             }
36967         }
36968          
36969         return el;
36970     },
36971
36972     updateColumns : function(){
36973         this.grid.stopEditing();
36974         var cm = this.grid.colModel, colIds = this.getColumnIds();
36975         //var totalWidth = cm.getTotalWidth();
36976         var pos = 0;
36977         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36978             //if(cm.isHidden(i)) continue;
36979             var w = cm.getColumnWidth(i);
36980             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36981             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36982         }
36983         this.updateSplitters();
36984     },
36985
36986     generateRules : function(cm){
36987         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
36988         Roo.util.CSS.removeStyleSheet(rulesId);
36989         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36990             var cid = cm.getColumnId(i);
36991             var align = '';
36992             if(cm.config[i].align){
36993                 align = 'text-align:'+cm.config[i].align+';';
36994             }
36995             var hidden = '';
36996             if(cm.isHidden(i)){
36997                 hidden = 'display:none;';
36998             }
36999             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37000             ruleBuf.push(
37001                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37002                     this.hdSelector, cid, " {\n", align, width, "}\n",
37003                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37004                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37005         }
37006         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37007     },
37008
37009     updateSplitters : function(){
37010         var cm = this.cm, s = this.getSplitters();
37011         if(s){ // splitters not created yet
37012             var pos = 0, locked = true;
37013             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37014                 if(cm.isHidden(i)) continue;
37015                 var w = cm.getColumnWidth(i); // make sure it's a number
37016                 if(!cm.isLocked(i) && locked){
37017                     pos = 0;
37018                     locked = false;
37019                 }
37020                 pos += w;
37021                 s[i].style.left = (pos-this.splitOffset) + "px";
37022             }
37023         }
37024     },
37025
37026     handleHiddenChange : function(colModel, colIndex, hidden){
37027         if(hidden){
37028             this.hideColumn(colIndex);
37029         }else{
37030             this.unhideColumn(colIndex);
37031         }
37032     },
37033
37034     hideColumn : function(colIndex){
37035         var cid = this.getColumnId(colIndex);
37036         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37037         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37038         if(Roo.isSafari){
37039             this.updateHeaders();
37040         }
37041         this.updateSplitters();
37042         this.layout();
37043     },
37044
37045     unhideColumn : function(colIndex){
37046         var cid = this.getColumnId(colIndex);
37047         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37048         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37049
37050         if(Roo.isSafari){
37051             this.updateHeaders();
37052         }
37053         this.updateSplitters();
37054         this.layout();
37055     },
37056
37057     insertRows : function(dm, firstRow, lastRow, isUpdate){
37058         if(firstRow == 0 && lastRow == dm.getCount()-1){
37059             this.refresh();
37060         }else{
37061             if(!isUpdate){
37062                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37063             }
37064             var s = this.getScrollState();
37065             var markup = this.renderRows(firstRow, lastRow);
37066             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37067             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37068             this.restoreScroll(s);
37069             if(!isUpdate){
37070                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37071                 this.syncRowHeights(firstRow, lastRow);
37072                 this.stripeRows(firstRow);
37073                 this.layout();
37074             }
37075         }
37076     },
37077
37078     bufferRows : function(markup, target, index){
37079         var before = null, trows = target.rows, tbody = target.tBodies[0];
37080         if(index < trows.length){
37081             before = trows[index];
37082         }
37083         var b = document.createElement("div");
37084         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37085         var rows = b.firstChild.rows;
37086         for(var i = 0, len = rows.length; i < len; i++){
37087             if(before){
37088                 tbody.insertBefore(rows[0], before);
37089             }else{
37090                 tbody.appendChild(rows[0]);
37091             }
37092         }
37093         b.innerHTML = "";
37094         b = null;
37095     },
37096
37097     deleteRows : function(dm, firstRow, lastRow){
37098         if(dm.getRowCount()<1){
37099             this.fireEvent("beforerefresh", this);
37100             this.mainBody.update("");
37101             this.lockedBody.update("");
37102             this.fireEvent("refresh", this);
37103         }else{
37104             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37105             var bt = this.getBodyTable();
37106             var tbody = bt.firstChild;
37107             var rows = bt.rows;
37108             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37109                 tbody.removeChild(rows[firstRow]);
37110             }
37111             this.stripeRows(firstRow);
37112             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37113         }
37114     },
37115
37116     updateRows : function(dataSource, firstRow, lastRow){
37117         var s = this.getScrollState();
37118         this.refresh();
37119         this.restoreScroll(s);
37120     },
37121
37122     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37123         if(!noRefresh){
37124            this.refresh();
37125         }
37126         this.updateHeaderSortState();
37127     },
37128
37129     getScrollState : function(){
37130         
37131         var sb = this.scroller.dom;
37132         return {left: sb.scrollLeft, top: sb.scrollTop};
37133     },
37134
37135     stripeRows : function(startRow){
37136         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37137             return;
37138         }
37139         startRow = startRow || 0;
37140         var rows = this.getBodyTable().rows;
37141         var lrows = this.getLockedTable().rows;
37142         var cls = ' x-grid-row-alt ';
37143         for(var i = startRow, len = rows.length; i < len; i++){
37144             var row = rows[i], lrow = lrows[i];
37145             var isAlt = ((i+1) % 2 == 0);
37146             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37147             if(isAlt == hasAlt){
37148                 continue;
37149             }
37150             if(isAlt){
37151                 row.className += " x-grid-row-alt";
37152             }else{
37153                 row.className = row.className.replace("x-grid-row-alt", "");
37154             }
37155             if(lrow){
37156                 lrow.className = row.className;
37157             }
37158         }
37159     },
37160
37161     restoreScroll : function(state){
37162         //Roo.log('GridView.restoreScroll');
37163         var sb = this.scroller.dom;
37164         sb.scrollLeft = state.left;
37165         sb.scrollTop = state.top;
37166         this.syncScroll();
37167     },
37168
37169     syncScroll : function(){
37170         //Roo.log('GridView.syncScroll');
37171         var sb = this.scroller.dom;
37172         var sh = this.mainHd.dom;
37173         var bs = this.mainBody.dom;
37174         var lv = this.lockedBody.dom;
37175         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37176         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37177     },
37178
37179     handleScroll : function(e){
37180         this.syncScroll();
37181         var sb = this.scroller.dom;
37182         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37183         e.stopEvent();
37184     },
37185
37186     handleWheel : function(e){
37187         var d = e.getWheelDelta();
37188         this.scroller.dom.scrollTop -= d*22;
37189         // set this here to prevent jumpy scrolling on large tables
37190         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37191         e.stopEvent();
37192     },
37193
37194     renderRows : function(startRow, endRow){
37195         // pull in all the crap needed to render rows
37196         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37197         var colCount = cm.getColumnCount();
37198
37199         if(ds.getCount() < 1){
37200             return ["", ""];
37201         }
37202
37203         // build a map for all the columns
37204         var cs = [];
37205         for(var i = 0; i < colCount; i++){
37206             var name = cm.getDataIndex(i);
37207             cs[i] = {
37208                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37209                 renderer : cm.getRenderer(i),
37210                 id : cm.getColumnId(i),
37211                 locked : cm.isLocked(i)
37212             };
37213         }
37214
37215         startRow = startRow || 0;
37216         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37217
37218         // records to render
37219         var rs = ds.getRange(startRow, endRow);
37220
37221         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37222     },
37223
37224     // As much as I hate to duplicate code, this was branched because FireFox really hates
37225     // [].join("") on strings. The performance difference was substantial enough to
37226     // branch this function
37227     doRender : Roo.isGecko ?
37228             function(cs, rs, ds, startRow, colCount, stripe){
37229                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37230                 // buffers
37231                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37232                 
37233                 var hasListener = this.grid.hasListener('rowclass');
37234                 var rowcfg = {};
37235                 for(var j = 0, len = rs.length; j < len; j++){
37236                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37237                     for(var i = 0; i < colCount; i++){
37238                         c = cs[i];
37239                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37240                         p.id = c.id;
37241                         p.css = p.attr = "";
37242                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37243                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37244                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37245                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37246                         }
37247                         var markup = ct.apply(p);
37248                         if(!c.locked){
37249                             cb+= markup;
37250                         }else{
37251                             lcb+= markup;
37252                         }
37253                     }
37254                     var alt = [];
37255                     if(stripe && ((rowIndex+1) % 2 == 0)){
37256                         alt.push("x-grid-row-alt")
37257                     }
37258                     if(r.dirty){
37259                         alt.push(  " x-grid-dirty-row");
37260                     }
37261                     rp.cells = lcb;
37262                     if(this.getRowClass){
37263                         alt.push(this.getRowClass(r, rowIndex));
37264                     }
37265                     if (hasListener) {
37266                         rowcfg = {
37267                              
37268                             record: r,
37269                             rowIndex : rowIndex,
37270                             rowClass : ''
37271                         }
37272                         this.grid.fireEvent('rowclass', this, rowcfg);
37273                         alt.push(rowcfg.rowClass);
37274                     }
37275                     rp.alt = alt.join(" ");
37276                     lbuf+= rt.apply(rp);
37277                     rp.cells = cb;
37278                     buf+=  rt.apply(rp);
37279                 }
37280                 return [lbuf, buf];
37281             } :
37282             function(cs, rs, ds, startRow, colCount, stripe){
37283                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37284                 // buffers
37285                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37286                 var hasListener = this.grid.hasListener('rowclass');
37287  
37288                 var rowcfg = {};
37289                 for(var j = 0, len = rs.length; j < len; j++){
37290                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37291                     for(var i = 0; i < colCount; i++){
37292                         c = cs[i];
37293                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37294                         p.id = c.id;
37295                         p.css = p.attr = "";
37296                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37297                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37298                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37299                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37300                         }
37301                         
37302                         var markup = ct.apply(p);
37303                         if(!c.locked){
37304                             cb[cb.length] = markup;
37305                         }else{
37306                             lcb[lcb.length] = markup;
37307                         }
37308                     }
37309                     var alt = [];
37310                     if(stripe && ((rowIndex+1) % 2 == 0)){
37311                         alt.push( "x-grid-row-alt");
37312                     }
37313                     if(r.dirty){
37314                         alt.push(" x-grid-dirty-row");
37315                     }
37316                     rp.cells = lcb;
37317                     if(this.getRowClass){
37318                         alt.push( this.getRowClass(r, rowIndex));
37319                     }
37320                     if (hasListener) {
37321                         rowcfg = {
37322                              
37323                             record: r,
37324                             rowIndex : rowIndex,
37325                             rowClass : ''
37326                         }
37327                         this.grid.fireEvent('rowclass', this, rowcfg);
37328                         alt.push(rowcfg.rowClass);
37329                     }
37330                     rp.alt = alt.join(" ");
37331                     rp.cells = lcb.join("");
37332                     lbuf[lbuf.length] = rt.apply(rp);
37333                     rp.cells = cb.join("");
37334                     buf[buf.length] =  rt.apply(rp);
37335                 }
37336                 return [lbuf.join(""), buf.join("")];
37337             },
37338
37339     renderBody : function(){
37340         var markup = this.renderRows();
37341         var bt = this.templates.body;
37342         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37343     },
37344
37345     /**
37346      * Refreshes the grid
37347      * @param {Boolean} headersToo
37348      */
37349     refresh : function(headersToo){
37350         this.fireEvent("beforerefresh", this);
37351         this.grid.stopEditing();
37352         var result = this.renderBody();
37353         this.lockedBody.update(result[0]);
37354         this.mainBody.update(result[1]);
37355         if(headersToo === true){
37356             this.updateHeaders();
37357             this.updateColumns();
37358             this.updateSplitters();
37359             this.updateHeaderSortState();
37360         }
37361         this.syncRowHeights();
37362         this.layout();
37363         this.fireEvent("refresh", this);
37364     },
37365
37366     handleColumnMove : function(cm, oldIndex, newIndex){
37367         this.indexMap = null;
37368         var s = this.getScrollState();
37369         this.refresh(true);
37370         this.restoreScroll(s);
37371         this.afterMove(newIndex);
37372     },
37373
37374     afterMove : function(colIndex){
37375         if(this.enableMoveAnim && Roo.enableFx){
37376             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37377         }
37378         // if multisort - fix sortOrder, and reload..
37379         if (this.grid.dataSource.multiSort) {
37380             // the we can call sort again..
37381             var dm = this.grid.dataSource;
37382             var cm = this.grid.colModel;
37383             var so = [];
37384             for(var i = 0; i < cm.config.length; i++ ) {
37385                 
37386                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37387                     continue; // dont' bother, it's not in sort list or being set.
37388                 }
37389                 
37390                 so.push(cm.config[i].dataIndex);
37391             };
37392             dm.sortOrder = so;
37393             dm.load(dm.lastOptions);
37394             
37395             
37396         }
37397         
37398     },
37399
37400     updateCell : function(dm, rowIndex, dataIndex){
37401         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37402         if(typeof colIndex == "undefined"){ // not present in grid
37403             return;
37404         }
37405         var cm = this.grid.colModel;
37406         var cell = this.getCell(rowIndex, colIndex);
37407         var cellText = this.getCellText(rowIndex, colIndex);
37408
37409         var p = {
37410             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37411             id : cm.getColumnId(colIndex),
37412             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37413         };
37414         var renderer = cm.getRenderer(colIndex);
37415         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37416         if(typeof val == "undefined" || val === "") val = "&#160;";
37417         cellText.innerHTML = val;
37418         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37419         this.syncRowHeights(rowIndex, rowIndex);
37420     },
37421
37422     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37423         var maxWidth = 0;
37424         if(this.grid.autoSizeHeaders){
37425             var h = this.getHeaderCellMeasure(colIndex);
37426             maxWidth = Math.max(maxWidth, h.scrollWidth);
37427         }
37428         var tb, index;
37429         if(this.cm.isLocked(colIndex)){
37430             tb = this.getLockedTable();
37431             index = colIndex;
37432         }else{
37433             tb = this.getBodyTable();
37434             index = colIndex - this.cm.getLockedCount();
37435         }
37436         if(tb && tb.rows){
37437             var rows = tb.rows;
37438             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37439             for(var i = 0; i < stopIndex; i++){
37440                 var cell = rows[i].childNodes[index].firstChild;
37441                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37442             }
37443         }
37444         return maxWidth + /*margin for error in IE*/ 5;
37445     },
37446     /**
37447      * Autofit a column to its content.
37448      * @param {Number} colIndex
37449      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37450      */
37451      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37452          if(this.cm.isHidden(colIndex)){
37453              return; // can't calc a hidden column
37454          }
37455         if(forceMinSize){
37456             var cid = this.cm.getColumnId(colIndex);
37457             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37458            if(this.grid.autoSizeHeaders){
37459                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37460            }
37461         }
37462         var newWidth = this.calcColumnWidth(colIndex);
37463         this.cm.setColumnWidth(colIndex,
37464             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37465         if(!suppressEvent){
37466             this.grid.fireEvent("columnresize", colIndex, newWidth);
37467         }
37468     },
37469
37470     /**
37471      * Autofits all columns to their content and then expands to fit any extra space in the grid
37472      */
37473      autoSizeColumns : function(){
37474         var cm = this.grid.colModel;
37475         var colCount = cm.getColumnCount();
37476         for(var i = 0; i < colCount; i++){
37477             this.autoSizeColumn(i, true, true);
37478         }
37479         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37480             this.fitColumns();
37481         }else{
37482             this.updateColumns();
37483             this.layout();
37484         }
37485     },
37486
37487     /**
37488      * Autofits all columns to the grid's width proportionate with their current size
37489      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37490      */
37491     fitColumns : function(reserveScrollSpace){
37492         var cm = this.grid.colModel;
37493         var colCount = cm.getColumnCount();
37494         var cols = [];
37495         var width = 0;
37496         var i, w;
37497         for (i = 0; i < colCount; i++){
37498             if(!cm.isHidden(i) && !cm.isFixed(i)){
37499                 w = cm.getColumnWidth(i);
37500                 cols.push(i);
37501                 cols.push(w);
37502                 width += w;
37503             }
37504         }
37505         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37506         if(reserveScrollSpace){
37507             avail -= 17;
37508         }
37509         var frac = (avail - cm.getTotalWidth())/width;
37510         while (cols.length){
37511             w = cols.pop();
37512             i = cols.pop();
37513             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37514         }
37515         this.updateColumns();
37516         this.layout();
37517     },
37518
37519     onRowSelect : function(rowIndex){
37520         var row = this.getRowComposite(rowIndex);
37521         row.addClass("x-grid-row-selected");
37522     },
37523
37524     onRowDeselect : function(rowIndex){
37525         var row = this.getRowComposite(rowIndex);
37526         row.removeClass("x-grid-row-selected");
37527     },
37528
37529     onCellSelect : function(row, col){
37530         var cell = this.getCell(row, col);
37531         if(cell){
37532             Roo.fly(cell).addClass("x-grid-cell-selected");
37533         }
37534     },
37535
37536     onCellDeselect : function(row, col){
37537         var cell = this.getCell(row, col);
37538         if(cell){
37539             Roo.fly(cell).removeClass("x-grid-cell-selected");
37540         }
37541     },
37542
37543     updateHeaderSortState : function(){
37544         
37545         // sort state can be single { field: xxx, direction : yyy}
37546         // or   { xxx=>ASC , yyy : DESC ..... }
37547         
37548         var mstate = {};
37549         if (!this.ds.multiSort) { 
37550             var state = this.ds.getSortState();
37551             if(!state){
37552                 return;
37553             }
37554             mstate[state.field] = state.direction;
37555             // FIXME... - this is not used here.. but might be elsewhere..
37556             this.sortState = state;
37557             
37558         } else {
37559             mstate = this.ds.sortToggle;
37560         }
37561         //remove existing sort classes..
37562         
37563         var sc = this.sortClasses;
37564         var hds = this.el.select(this.headerSelector).removeClass(sc);
37565         
37566         for(var f in mstate) {
37567         
37568             var sortColumn = this.cm.findColumnIndex(f);
37569             
37570             if(sortColumn != -1){
37571                 var sortDir = mstate[f];        
37572                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37573             }
37574         }
37575         
37576          
37577         
37578     },
37579
37580
37581     handleHeaderClick : function(g, index,e){
37582         
37583         Roo.log("header click");
37584         
37585         if (Roo.isTouch) {
37586             // touch events on header are handled by context
37587             this.handleHdCtx(g,index,e);
37588             return;
37589         }
37590         
37591         
37592         if(this.headersDisabled){
37593             return;
37594         }
37595         var dm = g.dataSource, cm = g.colModel;
37596         if(!cm.isSortable(index)){
37597             return;
37598         }
37599         g.stopEditing();
37600         
37601         if (dm.multiSort) {
37602             // update the sortOrder
37603             var so = [];
37604             for(var i = 0; i < cm.config.length; i++ ) {
37605                 
37606                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37607                     continue; // dont' bother, it's not in sort list or being set.
37608                 }
37609                 
37610                 so.push(cm.config[i].dataIndex);
37611             };
37612             dm.sortOrder = so;
37613         }
37614         
37615         
37616         dm.sort(cm.getDataIndex(index));
37617     },
37618
37619
37620     destroy : function(){
37621         if(this.colMenu){
37622             this.colMenu.removeAll();
37623             Roo.menu.MenuMgr.unregister(this.colMenu);
37624             this.colMenu.getEl().remove();
37625             delete this.colMenu;
37626         }
37627         if(this.hmenu){
37628             this.hmenu.removeAll();
37629             Roo.menu.MenuMgr.unregister(this.hmenu);
37630             this.hmenu.getEl().remove();
37631             delete this.hmenu;
37632         }
37633         if(this.grid.enableColumnMove){
37634             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37635             if(dds){
37636                 for(var dd in dds){
37637                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37638                         var elid = dds[dd].dragElId;
37639                         dds[dd].unreg();
37640                         Roo.get(elid).remove();
37641                     } else if(dds[dd].config.isTarget){
37642                         dds[dd].proxyTop.remove();
37643                         dds[dd].proxyBottom.remove();
37644                         dds[dd].unreg();
37645                     }
37646                     if(Roo.dd.DDM.locationCache[dd]){
37647                         delete Roo.dd.DDM.locationCache[dd];
37648                     }
37649                 }
37650                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37651             }
37652         }
37653         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37654         this.bind(null, null);
37655         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37656     },
37657
37658     handleLockChange : function(){
37659         this.refresh(true);
37660     },
37661
37662     onDenyColumnLock : function(){
37663
37664     },
37665
37666     onDenyColumnHide : function(){
37667
37668     },
37669
37670     handleHdMenuClick : function(item){
37671         var index = this.hdCtxIndex;
37672         var cm = this.cm, ds = this.ds;
37673         switch(item.id){
37674             case "asc":
37675                 ds.sort(cm.getDataIndex(index), "ASC");
37676                 break;
37677             case "desc":
37678                 ds.sort(cm.getDataIndex(index), "DESC");
37679                 break;
37680             case "lock":
37681                 var lc = cm.getLockedCount();
37682                 if(cm.getColumnCount(true) <= lc+1){
37683                     this.onDenyColumnLock();
37684                     return;
37685                 }
37686                 if(lc != index){
37687                     cm.setLocked(index, true, true);
37688                     cm.moveColumn(index, lc);
37689                     this.grid.fireEvent("columnmove", index, lc);
37690                 }else{
37691                     cm.setLocked(index, true);
37692                 }
37693             break;
37694             case "unlock":
37695                 var lc = cm.getLockedCount();
37696                 if((lc-1) != index){
37697                     cm.setLocked(index, false, true);
37698                     cm.moveColumn(index, lc-1);
37699                     this.grid.fireEvent("columnmove", index, lc-1);
37700                 }else{
37701                     cm.setLocked(index, false);
37702                 }
37703             break;
37704             case 'wider': // used to expand cols on touch..
37705             case 'narrow':
37706                 var cw = cm.getColumnWidth(index);
37707                 cw += (item.id == 'wider' ? 1 : -1) * 50;
37708                 cw = Math.max(0, cw);
37709                 cw = Math.min(cw,4000);
37710                 cm.setColumnWidth(index, cw);
37711                 break;
37712                 
37713             default:
37714                 index = cm.getIndexById(item.id.substr(4));
37715                 if(index != -1){
37716                     if(item.checked && cm.getColumnCount(true) <= 1){
37717                         this.onDenyColumnHide();
37718                         return false;
37719                     }
37720                     cm.setHidden(index, item.checked);
37721                 }
37722         }
37723         return true;
37724     },
37725
37726     beforeColMenuShow : function(){
37727         var cm = this.cm,  colCount = cm.getColumnCount();
37728         this.colMenu.removeAll();
37729         for(var i = 0; i < colCount; i++){
37730             this.colMenu.add(new Roo.menu.CheckItem({
37731                 id: "col-"+cm.getColumnId(i),
37732                 text: cm.getColumnHeader(i),
37733                 checked: !cm.isHidden(i),
37734                 hideOnClick:false
37735             }));
37736         }
37737     },
37738
37739     handleHdCtx : function(g, index, e){
37740         e.stopEvent();
37741         var hd = this.getHeaderCell(index);
37742         this.hdCtxIndex = index;
37743         var ms = this.hmenu.items, cm = this.cm;
37744         ms.get("asc").setDisabled(!cm.isSortable(index));
37745         ms.get("desc").setDisabled(!cm.isSortable(index));
37746         if(this.grid.enableColLock !== false){
37747             ms.get("lock").setDisabled(cm.isLocked(index));
37748             ms.get("unlock").setDisabled(!cm.isLocked(index));
37749         }
37750         this.hmenu.show(hd, "tl-bl");
37751     },
37752
37753     handleHdOver : function(e){
37754         var hd = this.findHeaderCell(e.getTarget());
37755         if(hd && !this.headersDisabled){
37756             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37757                this.fly(hd).addClass("x-grid-hd-over");
37758             }
37759         }
37760     },
37761
37762     handleHdOut : function(e){
37763         var hd = this.findHeaderCell(e.getTarget());
37764         if(hd){
37765             this.fly(hd).removeClass("x-grid-hd-over");
37766         }
37767     },
37768
37769     handleSplitDblClick : function(e, t){
37770         var i = this.getCellIndex(t);
37771         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37772             this.autoSizeColumn(i, true);
37773             this.layout();
37774         }
37775     },
37776
37777     render : function(){
37778
37779         var cm = this.cm;
37780         var colCount = cm.getColumnCount();
37781
37782         if(this.grid.monitorWindowResize === true){
37783             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37784         }
37785         var header = this.renderHeaders();
37786         var body = this.templates.body.apply({rows:""});
37787         var html = this.templates.master.apply({
37788             lockedBody: body,
37789             body: body,
37790             lockedHeader: header[0],
37791             header: header[1]
37792         });
37793
37794         //this.updateColumns();
37795
37796         this.grid.getGridEl().dom.innerHTML = html;
37797
37798         this.initElements();
37799         
37800         // a kludge to fix the random scolling effect in webkit
37801         this.el.on("scroll", function() {
37802             this.el.dom.scrollTop=0; // hopefully not recursive..
37803         },this);
37804
37805         this.scroller.on("scroll", this.handleScroll, this);
37806         this.lockedBody.on("mousewheel", this.handleWheel, this);
37807         this.mainBody.on("mousewheel", this.handleWheel, this);
37808
37809         this.mainHd.on("mouseover", this.handleHdOver, this);
37810         this.mainHd.on("mouseout", this.handleHdOut, this);
37811         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37812                 {delegate: "."+this.splitClass});
37813
37814         this.lockedHd.on("mouseover", this.handleHdOver, this);
37815         this.lockedHd.on("mouseout", this.handleHdOut, this);
37816         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37817                 {delegate: "."+this.splitClass});
37818
37819         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37820             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37821         }
37822
37823         this.updateSplitters();
37824
37825         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37826             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37827             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37828         }
37829
37830         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37831             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37832             this.hmenu.add(
37833                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37834                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37835             );
37836             if(this.grid.enableColLock !== false){
37837                 this.hmenu.add('-',
37838                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37839                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37840                 );
37841             }
37842             if (Roo.isTouch) {
37843                  this.hmenu.add('-',
37844                     {id:"wider", text: this.columnsWiderText},
37845                     {id:"narrow", text: this.columnsNarrowText }
37846                 );
37847                 
37848                  
37849             }
37850             
37851             if(this.grid.enableColumnHide !== false){
37852
37853                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37854                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37855                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37856
37857                 this.hmenu.add('-',
37858                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37859                 );
37860             }
37861             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37862
37863             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37864         }
37865
37866         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37867             this.dd = new Roo.grid.GridDragZone(this.grid, {
37868                 ddGroup : this.grid.ddGroup || 'GridDD'
37869             });
37870             
37871         }
37872
37873         /*
37874         for(var i = 0; i < colCount; i++){
37875             if(cm.isHidden(i)){
37876                 this.hideColumn(i);
37877             }
37878             if(cm.config[i].align){
37879                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37880                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37881             }
37882         }*/
37883         
37884         this.updateHeaderSortState();
37885
37886         this.beforeInitialResize();
37887         this.layout(true);
37888
37889         // two part rendering gives faster view to the user
37890         this.renderPhase2.defer(1, this);
37891     },
37892
37893     renderPhase2 : function(){
37894         // render the rows now
37895         this.refresh();
37896         if(this.grid.autoSizeColumns){
37897             this.autoSizeColumns();
37898         }
37899     },
37900
37901     beforeInitialResize : function(){
37902
37903     },
37904
37905     onColumnSplitterMoved : function(i, w){
37906         this.userResized = true;
37907         var cm = this.grid.colModel;
37908         cm.setColumnWidth(i, w, true);
37909         var cid = cm.getColumnId(i);
37910         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37911         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37912         this.updateSplitters();
37913         this.layout();
37914         this.grid.fireEvent("columnresize", i, w);
37915     },
37916
37917     syncRowHeights : function(startIndex, endIndex){
37918         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37919             startIndex = startIndex || 0;
37920             var mrows = this.getBodyTable().rows;
37921             var lrows = this.getLockedTable().rows;
37922             var len = mrows.length-1;
37923             endIndex = Math.min(endIndex || len, len);
37924             for(var i = startIndex; i <= endIndex; i++){
37925                 var m = mrows[i], l = lrows[i];
37926                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37927                 m.style.height = l.style.height = h + "px";
37928             }
37929         }
37930     },
37931
37932     layout : function(initialRender, is2ndPass){
37933         var g = this.grid;
37934         var auto = g.autoHeight;
37935         var scrollOffset = 16;
37936         var c = g.getGridEl(), cm = this.cm,
37937                 expandCol = g.autoExpandColumn,
37938                 gv = this;
37939         //c.beginMeasure();
37940
37941         if(!c.dom.offsetWidth){ // display:none?
37942             if(initialRender){
37943                 this.lockedWrap.show();
37944                 this.mainWrap.show();
37945             }
37946             return;
37947         }
37948
37949         var hasLock = this.cm.isLocked(0);
37950
37951         var tbh = this.headerPanel.getHeight();
37952         var bbh = this.footerPanel.getHeight();
37953
37954         if(auto){
37955             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
37956             var newHeight = ch + c.getBorderWidth("tb");
37957             if(g.maxHeight){
37958                 newHeight = Math.min(g.maxHeight, newHeight);
37959             }
37960             c.setHeight(newHeight);
37961         }
37962
37963         if(g.autoWidth){
37964             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
37965         }
37966
37967         var s = this.scroller;
37968
37969         var csize = c.getSize(true);
37970
37971         this.el.setSize(csize.width, csize.height);
37972
37973         this.headerPanel.setWidth(csize.width);
37974         this.footerPanel.setWidth(csize.width);
37975
37976         var hdHeight = this.mainHd.getHeight();
37977         var vw = csize.width;
37978         var vh = csize.height - (tbh + bbh);
37979
37980         s.setSize(vw, vh);
37981
37982         var bt = this.getBodyTable();
37983         var ltWidth = hasLock ?
37984                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
37985
37986         var scrollHeight = bt.offsetHeight;
37987         var scrollWidth = ltWidth + bt.offsetWidth;
37988         var vscroll = false, hscroll = false;
37989
37990         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
37991
37992         var lw = this.lockedWrap, mw = this.mainWrap;
37993         var lb = this.lockedBody, mb = this.mainBody;
37994
37995         setTimeout(function(){
37996             var t = s.dom.offsetTop;
37997             var w = s.dom.clientWidth,
37998                 h = s.dom.clientHeight;
37999
38000             lw.setTop(t);
38001             lw.setSize(ltWidth, h);
38002
38003             mw.setLeftTop(ltWidth, t);
38004             mw.setSize(w-ltWidth, h);
38005
38006             lb.setHeight(h-hdHeight);
38007             mb.setHeight(h-hdHeight);
38008
38009             if(is2ndPass !== true && !gv.userResized && expandCol){
38010                 // high speed resize without full column calculation
38011                 
38012                 var ci = cm.getIndexById(expandCol);
38013                 if (ci < 0) {
38014                     ci = cm.findColumnIndex(expandCol);
38015                 }
38016                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38017                 var expandId = cm.getColumnId(ci);
38018                 var  tw = cm.getTotalWidth(false);
38019                 var currentWidth = cm.getColumnWidth(ci);
38020                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38021                 if(currentWidth != cw){
38022                     cm.setColumnWidth(ci, cw, true);
38023                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38024                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38025                     gv.updateSplitters();
38026                     gv.layout(false, true);
38027                 }
38028             }
38029
38030             if(initialRender){
38031                 lw.show();
38032                 mw.show();
38033             }
38034             //c.endMeasure();
38035         }, 10);
38036     },
38037
38038     onWindowResize : function(){
38039         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38040             return;
38041         }
38042         this.layout();
38043     },
38044
38045     appendFooter : function(parentEl){
38046         return null;
38047     },
38048
38049     sortAscText : "Sort Ascending",
38050     sortDescText : "Sort Descending",
38051     lockText : "Lock Column",
38052     unlockText : "Unlock Column",
38053     columnsText : "Columns",
38054  
38055     columnsWiderText : "Wider",
38056     columnsNarrowText : "Thinner"
38057 });
38058
38059
38060 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38061     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38062     this.proxy.el.addClass('x-grid3-col-dd');
38063 };
38064
38065 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38066     handleMouseDown : function(e){
38067
38068     },
38069
38070     callHandleMouseDown : function(e){
38071         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38072     }
38073 });
38074 /*
38075  * Based on:
38076  * Ext JS Library 1.1.1
38077  * Copyright(c) 2006-2007, Ext JS, LLC.
38078  *
38079  * Originally Released Under LGPL - original licence link has changed is not relivant.
38080  *
38081  * Fork - LGPL
38082  * <script type="text/javascript">
38083  */
38084  
38085 // private
38086 // This is a support class used internally by the Grid components
38087 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38088     this.grid = grid;
38089     this.view = grid.getView();
38090     this.proxy = this.view.resizeProxy;
38091     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38092         "gridSplitters" + this.grid.getGridEl().id, {
38093         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38094     });
38095     this.setHandleElId(Roo.id(hd));
38096     this.setOuterHandleElId(Roo.id(hd2));
38097     this.scroll = false;
38098 };
38099 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38100     fly: Roo.Element.fly,
38101
38102     b4StartDrag : function(x, y){
38103         this.view.headersDisabled = true;
38104         this.proxy.setHeight(this.view.mainWrap.getHeight());
38105         var w = this.cm.getColumnWidth(this.cellIndex);
38106         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38107         this.resetConstraints();
38108         this.setXConstraint(minw, 1000);
38109         this.setYConstraint(0, 0);
38110         this.minX = x - minw;
38111         this.maxX = x + 1000;
38112         this.startPos = x;
38113         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38114     },
38115
38116
38117     handleMouseDown : function(e){
38118         ev = Roo.EventObject.setEvent(e);
38119         var t = this.fly(ev.getTarget());
38120         if(t.hasClass("x-grid-split")){
38121             this.cellIndex = this.view.getCellIndex(t.dom);
38122             this.split = t.dom;
38123             this.cm = this.grid.colModel;
38124             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38125                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38126             }
38127         }
38128     },
38129
38130     endDrag : function(e){
38131         this.view.headersDisabled = false;
38132         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38133         var diff = endX - this.startPos;
38134         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38135     },
38136
38137     autoOffset : function(){
38138         this.setDelta(0,0);
38139     }
38140 });/*
38141  * Based on:
38142  * Ext JS Library 1.1.1
38143  * Copyright(c) 2006-2007, Ext JS, LLC.
38144  *
38145  * Originally Released Under LGPL - original licence link has changed is not relivant.
38146  *
38147  * Fork - LGPL
38148  * <script type="text/javascript">
38149  */
38150  
38151 // private
38152 // This is a support class used internally by the Grid components
38153 Roo.grid.GridDragZone = function(grid, config){
38154     this.view = grid.getView();
38155     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38156     if(this.view.lockedBody){
38157         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38158         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38159     }
38160     this.scroll = false;
38161     this.grid = grid;
38162     this.ddel = document.createElement('div');
38163     this.ddel.className = 'x-grid-dd-wrap';
38164 };
38165
38166 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38167     ddGroup : "GridDD",
38168
38169     getDragData : function(e){
38170         var t = Roo.lib.Event.getTarget(e);
38171         var rowIndex = this.view.findRowIndex(t);
38172         var sm = this.grid.selModel;
38173             
38174         //Roo.log(rowIndex);
38175         
38176         if (sm.getSelectedCell) {
38177             // cell selection..
38178             if (!sm.getSelectedCell()) {
38179                 return false;
38180             }
38181             if (rowIndex != sm.getSelectedCell()[0]) {
38182                 return false;
38183             }
38184         
38185         }
38186         
38187         if(rowIndex !== false){
38188             
38189             // if editorgrid.. 
38190             
38191             
38192             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38193                
38194             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38195               //  
38196             //}
38197             if (e.hasModifier()){
38198                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38199             }
38200             
38201             Roo.log("getDragData");
38202             
38203             return {
38204                 grid: this.grid,
38205                 ddel: this.ddel,
38206                 rowIndex: rowIndex,
38207                 selections:sm.getSelections ? sm.getSelections() : (
38208                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38209                 )
38210             };
38211         }
38212         return false;
38213     },
38214
38215     onInitDrag : function(e){
38216         var data = this.dragData;
38217         this.ddel.innerHTML = this.grid.getDragDropText();
38218         this.proxy.update(this.ddel);
38219         // fire start drag?
38220     },
38221
38222     afterRepair : function(){
38223         this.dragging = false;
38224     },
38225
38226     getRepairXY : function(e, data){
38227         return false;
38228     },
38229
38230     onEndDrag : function(data, e){
38231         // fire end drag?
38232     },
38233
38234     onValidDrop : function(dd, e, id){
38235         // fire drag drop?
38236         this.hideProxy();
38237     },
38238
38239     beforeInvalidDrop : function(e, id){
38240
38241     }
38242 });/*
38243  * Based on:
38244  * Ext JS Library 1.1.1
38245  * Copyright(c) 2006-2007, Ext JS, LLC.
38246  *
38247  * Originally Released Under LGPL - original licence link has changed is not relivant.
38248  *
38249  * Fork - LGPL
38250  * <script type="text/javascript">
38251  */
38252  
38253
38254 /**
38255  * @class Roo.grid.ColumnModel
38256  * @extends Roo.util.Observable
38257  * This is the default implementation of a ColumnModel used by the Grid. It defines
38258  * the columns in the grid.
38259  * <br>Usage:<br>
38260  <pre><code>
38261  var colModel = new Roo.grid.ColumnModel([
38262         {header: "Ticker", width: 60, sortable: true, locked: true},
38263         {header: "Company Name", width: 150, sortable: true},
38264         {header: "Market Cap.", width: 100, sortable: true},
38265         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38266         {header: "Employees", width: 100, sortable: true, resizable: false}
38267  ]);
38268  </code></pre>
38269  * <p>
38270  
38271  * The config options listed for this class are options which may appear in each
38272  * individual column definition.
38273  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38274  * @constructor
38275  * @param {Object} config An Array of column config objects. See this class's
38276  * config objects for details.
38277 */
38278 Roo.grid.ColumnModel = function(config){
38279         /**
38280      * The config passed into the constructor
38281      */
38282     this.config = config;
38283     this.lookup = {};
38284
38285     // if no id, create one
38286     // if the column does not have a dataIndex mapping,
38287     // map it to the order it is in the config
38288     for(var i = 0, len = config.length; i < len; i++){
38289         var c = config[i];
38290         if(typeof c.dataIndex == "undefined"){
38291             c.dataIndex = i;
38292         }
38293         if(typeof c.renderer == "string"){
38294             c.renderer = Roo.util.Format[c.renderer];
38295         }
38296         if(typeof c.id == "undefined"){
38297             c.id = Roo.id();
38298         }
38299         if(c.editor && c.editor.xtype){
38300             c.editor  = Roo.factory(c.editor, Roo.grid);
38301         }
38302         if(c.editor && c.editor.isFormField){
38303             c.editor = new Roo.grid.GridEditor(c.editor);
38304         }
38305         this.lookup[c.id] = c;
38306     }
38307
38308     /**
38309      * The width of columns which have no width specified (defaults to 100)
38310      * @type Number
38311      */
38312     this.defaultWidth = 100;
38313
38314     /**
38315      * Default sortable of columns which have no sortable specified (defaults to false)
38316      * @type Boolean
38317      */
38318     this.defaultSortable = false;
38319
38320     this.addEvents({
38321         /**
38322              * @event widthchange
38323              * Fires when the width of a column changes.
38324              * @param {ColumnModel} this
38325              * @param {Number} columnIndex The column index
38326              * @param {Number} newWidth The new width
38327              */
38328             "widthchange": true,
38329         /**
38330              * @event headerchange
38331              * Fires when the text of a header changes.
38332              * @param {ColumnModel} this
38333              * @param {Number} columnIndex The column index
38334              * @param {Number} newText The new header text
38335              */
38336             "headerchange": true,
38337         /**
38338              * @event hiddenchange
38339              * Fires when a column is hidden or "unhidden".
38340              * @param {ColumnModel} this
38341              * @param {Number} columnIndex The column index
38342              * @param {Boolean} hidden true if hidden, false otherwise
38343              */
38344             "hiddenchange": true,
38345             /**
38346          * @event columnmoved
38347          * Fires when a column is moved.
38348          * @param {ColumnModel} this
38349          * @param {Number} oldIndex
38350          * @param {Number} newIndex
38351          */
38352         "columnmoved" : true,
38353         /**
38354          * @event columlockchange
38355          * Fires when a column's locked state is changed
38356          * @param {ColumnModel} this
38357          * @param {Number} colIndex
38358          * @param {Boolean} locked true if locked
38359          */
38360         "columnlockchange" : true
38361     });
38362     Roo.grid.ColumnModel.superclass.constructor.call(this);
38363 };
38364 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38365     /**
38366      * @cfg {String} header The header text to display in the Grid view.
38367      */
38368     /**
38369      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38370      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38371      * specified, the column's index is used as an index into the Record's data Array.
38372      */
38373     /**
38374      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38375      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38376      */
38377     /**
38378      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38379      * Defaults to the value of the {@link #defaultSortable} property.
38380      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38381      */
38382     /**
38383      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38384      */
38385     /**
38386      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38387      */
38388     /**
38389      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38390      */
38391     /**
38392      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38393      */
38394     /**
38395      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38396      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38397      * default renderer uses the raw data value.
38398      */
38399        /**
38400      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38401      */
38402     /**
38403      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38404      */
38405
38406     /**
38407      * Returns the id of the column at the specified index.
38408      * @param {Number} index The column index
38409      * @return {String} the id
38410      */
38411     getColumnId : function(index){
38412         return this.config[index].id;
38413     },
38414
38415     /**
38416      * Returns the column for a specified id.
38417      * @param {String} id The column id
38418      * @return {Object} the column
38419      */
38420     getColumnById : function(id){
38421         return this.lookup[id];
38422     },
38423
38424     
38425     /**
38426      * Returns the column for a specified dataIndex.
38427      * @param {String} dataIndex The column dataIndex
38428      * @return {Object|Boolean} the column or false if not found
38429      */
38430     getColumnByDataIndex: function(dataIndex){
38431         var index = this.findColumnIndex(dataIndex);
38432         return index > -1 ? this.config[index] : false;
38433     },
38434     
38435     /**
38436      * Returns the index for a specified column id.
38437      * @param {String} id The column id
38438      * @return {Number} the index, or -1 if not found
38439      */
38440     getIndexById : function(id){
38441         for(var i = 0, len = this.config.length; i < len; i++){
38442             if(this.config[i].id == id){
38443                 return i;
38444             }
38445         }
38446         return -1;
38447     },
38448     
38449     /**
38450      * Returns the index for a specified column dataIndex.
38451      * @param {String} dataIndex The column dataIndex
38452      * @return {Number} the index, or -1 if not found
38453      */
38454     
38455     findColumnIndex : function(dataIndex){
38456         for(var i = 0, len = this.config.length; i < len; i++){
38457             if(this.config[i].dataIndex == dataIndex){
38458                 return i;
38459             }
38460         }
38461         return -1;
38462     },
38463     
38464     
38465     moveColumn : function(oldIndex, newIndex){
38466         var c = this.config[oldIndex];
38467         this.config.splice(oldIndex, 1);
38468         this.config.splice(newIndex, 0, c);
38469         this.dataMap = null;
38470         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38471     },
38472
38473     isLocked : function(colIndex){
38474         return this.config[colIndex].locked === true;
38475     },
38476
38477     setLocked : function(colIndex, value, suppressEvent){
38478         if(this.isLocked(colIndex) == value){
38479             return;
38480         }
38481         this.config[colIndex].locked = value;
38482         if(!suppressEvent){
38483             this.fireEvent("columnlockchange", this, colIndex, value);
38484         }
38485     },
38486
38487     getTotalLockedWidth : function(){
38488         var totalWidth = 0;
38489         for(var i = 0; i < this.config.length; i++){
38490             if(this.isLocked(i) && !this.isHidden(i)){
38491                 this.totalWidth += this.getColumnWidth(i);
38492             }
38493         }
38494         return totalWidth;
38495     },
38496
38497     getLockedCount : function(){
38498         for(var i = 0, len = this.config.length; i < len; i++){
38499             if(!this.isLocked(i)){
38500                 return i;
38501             }
38502         }
38503     },
38504
38505     /**
38506      * Returns the number of columns.
38507      * @return {Number}
38508      */
38509     getColumnCount : function(visibleOnly){
38510         if(visibleOnly === true){
38511             var c = 0;
38512             for(var i = 0, len = this.config.length; i < len; i++){
38513                 if(!this.isHidden(i)){
38514                     c++;
38515                 }
38516             }
38517             return c;
38518         }
38519         return this.config.length;
38520     },
38521
38522     /**
38523      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38524      * @param {Function} fn
38525      * @param {Object} scope (optional)
38526      * @return {Array} result
38527      */
38528     getColumnsBy : function(fn, scope){
38529         var r = [];
38530         for(var i = 0, len = this.config.length; i < len; i++){
38531             var c = this.config[i];
38532             if(fn.call(scope||this, c, i) === true){
38533                 r[r.length] = c;
38534             }
38535         }
38536         return r;
38537     },
38538
38539     /**
38540      * Returns true if the specified column is sortable.
38541      * @param {Number} col The column index
38542      * @return {Boolean}
38543      */
38544     isSortable : function(col){
38545         if(typeof this.config[col].sortable == "undefined"){
38546             return this.defaultSortable;
38547         }
38548         return this.config[col].sortable;
38549     },
38550
38551     /**
38552      * Returns the rendering (formatting) function defined for the column.
38553      * @param {Number} col The column index.
38554      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38555      */
38556     getRenderer : function(col){
38557         if(!this.config[col].renderer){
38558             return Roo.grid.ColumnModel.defaultRenderer;
38559         }
38560         return this.config[col].renderer;
38561     },
38562
38563     /**
38564      * Sets the rendering (formatting) function for a column.
38565      * @param {Number} col The column index
38566      * @param {Function} fn The function to use to process the cell's raw data
38567      * to return HTML markup for the grid view. The render function is called with
38568      * the following parameters:<ul>
38569      * <li>Data value.</li>
38570      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38571      * <li>css A CSS style string to apply to the table cell.</li>
38572      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38573      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38574      * <li>Row index</li>
38575      * <li>Column index</li>
38576      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38577      */
38578     setRenderer : function(col, fn){
38579         this.config[col].renderer = fn;
38580     },
38581
38582     /**
38583      * Returns the width for the specified column.
38584      * @param {Number} col The column index
38585      * @return {Number}
38586      */
38587     getColumnWidth : function(col){
38588         return this.config[col].width * 1 || this.defaultWidth;
38589     },
38590
38591     /**
38592      * Sets the width for a column.
38593      * @param {Number} col The column index
38594      * @param {Number} width The new width
38595      */
38596     setColumnWidth : function(col, width, suppressEvent){
38597         this.config[col].width = width;
38598         this.totalWidth = null;
38599         if(!suppressEvent){
38600              this.fireEvent("widthchange", this, col, width);
38601         }
38602     },
38603
38604     /**
38605      * Returns the total width of all columns.
38606      * @param {Boolean} includeHidden True to include hidden column widths
38607      * @return {Number}
38608      */
38609     getTotalWidth : function(includeHidden){
38610         if(!this.totalWidth){
38611             this.totalWidth = 0;
38612             for(var i = 0, len = this.config.length; i < len; i++){
38613                 if(includeHidden || !this.isHidden(i)){
38614                     this.totalWidth += this.getColumnWidth(i);
38615                 }
38616             }
38617         }
38618         return this.totalWidth;
38619     },
38620
38621     /**
38622      * Returns the header for the specified column.
38623      * @param {Number} col The column index
38624      * @return {String}
38625      */
38626     getColumnHeader : function(col){
38627         return this.config[col].header;
38628     },
38629
38630     /**
38631      * Sets the header for a column.
38632      * @param {Number} col The column index
38633      * @param {String} header The new header
38634      */
38635     setColumnHeader : function(col, header){
38636         this.config[col].header = header;
38637         this.fireEvent("headerchange", this, col, header);
38638     },
38639
38640     /**
38641      * Returns the tooltip for the specified column.
38642      * @param {Number} col The column index
38643      * @return {String}
38644      */
38645     getColumnTooltip : function(col){
38646             return this.config[col].tooltip;
38647     },
38648     /**
38649      * Sets the tooltip for a column.
38650      * @param {Number} col The column index
38651      * @param {String} tooltip The new tooltip
38652      */
38653     setColumnTooltip : function(col, tooltip){
38654             this.config[col].tooltip = tooltip;
38655     },
38656
38657     /**
38658      * Returns the dataIndex for the specified column.
38659      * @param {Number} col The column index
38660      * @return {Number}
38661      */
38662     getDataIndex : function(col){
38663         return this.config[col].dataIndex;
38664     },
38665
38666     /**
38667      * Sets the dataIndex for a column.
38668      * @param {Number} col The column index
38669      * @param {Number} dataIndex The new dataIndex
38670      */
38671     setDataIndex : function(col, dataIndex){
38672         this.config[col].dataIndex = dataIndex;
38673     },
38674
38675     
38676     
38677     /**
38678      * Returns true if the cell is editable.
38679      * @param {Number} colIndex The column index
38680      * @param {Number} rowIndex The row index
38681      * @return {Boolean}
38682      */
38683     isCellEditable : function(colIndex, rowIndex){
38684         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38685     },
38686
38687     /**
38688      * Returns the editor defined for the cell/column.
38689      * return false or null to disable editing.
38690      * @param {Number} colIndex The column index
38691      * @param {Number} rowIndex The row index
38692      * @return {Object}
38693      */
38694     getCellEditor : function(colIndex, rowIndex){
38695         return this.config[colIndex].editor;
38696     },
38697
38698     /**
38699      * Sets if a column is editable.
38700      * @param {Number} col The column index
38701      * @param {Boolean} editable True if the column is editable
38702      */
38703     setEditable : function(col, editable){
38704         this.config[col].editable = editable;
38705     },
38706
38707
38708     /**
38709      * Returns true if the column is hidden.
38710      * @param {Number} colIndex The column index
38711      * @return {Boolean}
38712      */
38713     isHidden : function(colIndex){
38714         return this.config[colIndex].hidden;
38715     },
38716
38717
38718     /**
38719      * Returns true if the column width cannot be changed
38720      */
38721     isFixed : function(colIndex){
38722         return this.config[colIndex].fixed;
38723     },
38724
38725     /**
38726      * Returns true if the column can be resized
38727      * @return {Boolean}
38728      */
38729     isResizable : function(colIndex){
38730         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38731     },
38732     /**
38733      * Sets if a column is hidden.
38734      * @param {Number} colIndex The column index
38735      * @param {Boolean} hidden True if the column is hidden
38736      */
38737     setHidden : function(colIndex, hidden){
38738         this.config[colIndex].hidden = hidden;
38739         this.totalWidth = null;
38740         this.fireEvent("hiddenchange", this, colIndex, hidden);
38741     },
38742
38743     /**
38744      * Sets the editor for a column.
38745      * @param {Number} col The column index
38746      * @param {Object} editor The editor object
38747      */
38748     setEditor : function(col, editor){
38749         this.config[col].editor = editor;
38750     }
38751 });
38752
38753 Roo.grid.ColumnModel.defaultRenderer = function(value){
38754         if(typeof value == "string" && value.length < 1){
38755             return "&#160;";
38756         }
38757         return value;
38758 };
38759
38760 // Alias for backwards compatibility
38761 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38762 /*
38763  * Based on:
38764  * Ext JS Library 1.1.1
38765  * Copyright(c) 2006-2007, Ext JS, LLC.
38766  *
38767  * Originally Released Under LGPL - original licence link has changed is not relivant.
38768  *
38769  * Fork - LGPL
38770  * <script type="text/javascript">
38771  */
38772
38773 /**
38774  * @class Roo.grid.AbstractSelectionModel
38775  * @extends Roo.util.Observable
38776  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38777  * implemented by descendant classes.  This class should not be directly instantiated.
38778  * @constructor
38779  */
38780 Roo.grid.AbstractSelectionModel = function(){
38781     this.locked = false;
38782     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38783 };
38784
38785 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38786     /** @ignore Called by the grid automatically. Do not call directly. */
38787     init : function(grid){
38788         this.grid = grid;
38789         this.initEvents();
38790     },
38791
38792     /**
38793      * Locks the selections.
38794      */
38795     lock : function(){
38796         this.locked = true;
38797     },
38798
38799     /**
38800      * Unlocks the selections.
38801      */
38802     unlock : function(){
38803         this.locked = false;
38804     },
38805
38806     /**
38807      * Returns true if the selections are locked.
38808      * @return {Boolean}
38809      */
38810     isLocked : function(){
38811         return this.locked;
38812     }
38813 });/*
38814  * Based on:
38815  * Ext JS Library 1.1.1
38816  * Copyright(c) 2006-2007, Ext JS, LLC.
38817  *
38818  * Originally Released Under LGPL - original licence link has changed is not relivant.
38819  *
38820  * Fork - LGPL
38821  * <script type="text/javascript">
38822  */
38823 /**
38824  * @extends Roo.grid.AbstractSelectionModel
38825  * @class Roo.grid.RowSelectionModel
38826  * The default SelectionModel used by {@link Roo.grid.Grid}.
38827  * It supports multiple selections and keyboard selection/navigation. 
38828  * @constructor
38829  * @param {Object} config
38830  */
38831 Roo.grid.RowSelectionModel = function(config){
38832     Roo.apply(this, config);
38833     this.selections = new Roo.util.MixedCollection(false, function(o){
38834         return o.id;
38835     });
38836
38837     this.last = false;
38838     this.lastActive = false;
38839
38840     this.addEvents({
38841         /**
38842              * @event selectionchange
38843              * Fires when the selection changes
38844              * @param {SelectionModel} this
38845              */
38846             "selectionchange" : true,
38847         /**
38848              * @event afterselectionchange
38849              * Fires after the selection changes (eg. by key press or clicking)
38850              * @param {SelectionModel} this
38851              */
38852             "afterselectionchange" : true,
38853         /**
38854              * @event beforerowselect
38855              * Fires when a row is selected being selected, return false to cancel.
38856              * @param {SelectionModel} this
38857              * @param {Number} rowIndex The selected index
38858              * @param {Boolean} keepExisting False if other selections will be cleared
38859              */
38860             "beforerowselect" : true,
38861         /**
38862              * @event rowselect
38863              * Fires when a row is selected.
38864              * @param {SelectionModel} this
38865              * @param {Number} rowIndex The selected index
38866              * @param {Roo.data.Record} r The record
38867              */
38868             "rowselect" : true,
38869         /**
38870              * @event rowdeselect
38871              * Fires when a row is deselected.
38872              * @param {SelectionModel} this
38873              * @param {Number} rowIndex The selected index
38874              */
38875         "rowdeselect" : true
38876     });
38877     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38878     this.locked = false;
38879 };
38880
38881 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38882     /**
38883      * @cfg {Boolean} singleSelect
38884      * True to allow selection of only one row at a time (defaults to false)
38885      */
38886     singleSelect : false,
38887
38888     // private
38889     initEvents : function(){
38890
38891         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38892             this.grid.on("mousedown", this.handleMouseDown, this);
38893         }else{ // allow click to work like normal
38894             this.grid.on("rowclick", this.handleDragableRowClick, this);
38895         }
38896
38897         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38898             "up" : function(e){
38899                 if(!e.shiftKey){
38900                     this.selectPrevious(e.shiftKey);
38901                 }else if(this.last !== false && this.lastActive !== false){
38902                     var last = this.last;
38903                     this.selectRange(this.last,  this.lastActive-1);
38904                     this.grid.getView().focusRow(this.lastActive);
38905                     if(last !== false){
38906                         this.last = last;
38907                     }
38908                 }else{
38909                     this.selectFirstRow();
38910                 }
38911                 this.fireEvent("afterselectionchange", this);
38912             },
38913             "down" : function(e){
38914                 if(!e.shiftKey){
38915                     this.selectNext(e.shiftKey);
38916                 }else if(this.last !== false && this.lastActive !== false){
38917                     var last = this.last;
38918                     this.selectRange(this.last,  this.lastActive+1);
38919                     this.grid.getView().focusRow(this.lastActive);
38920                     if(last !== false){
38921                         this.last = last;
38922                     }
38923                 }else{
38924                     this.selectFirstRow();
38925                 }
38926                 this.fireEvent("afterselectionchange", this);
38927             },
38928             scope: this
38929         });
38930
38931         var view = this.grid.view;
38932         view.on("refresh", this.onRefresh, this);
38933         view.on("rowupdated", this.onRowUpdated, this);
38934         view.on("rowremoved", this.onRemove, this);
38935     },
38936
38937     // private
38938     onRefresh : function(){
38939         var ds = this.grid.dataSource, i, v = this.grid.view;
38940         var s = this.selections;
38941         s.each(function(r){
38942             if((i = ds.indexOfId(r.id)) != -1){
38943                 v.onRowSelect(i);
38944             }else{
38945                 s.remove(r);
38946             }
38947         });
38948     },
38949
38950     // private
38951     onRemove : function(v, index, r){
38952         this.selections.remove(r);
38953     },
38954
38955     // private
38956     onRowUpdated : function(v, index, r){
38957         if(this.isSelected(r)){
38958             v.onRowSelect(index);
38959         }
38960     },
38961
38962     /**
38963      * Select records.
38964      * @param {Array} records The records to select
38965      * @param {Boolean} keepExisting (optional) True to keep existing selections
38966      */
38967     selectRecords : function(records, keepExisting){
38968         if(!keepExisting){
38969             this.clearSelections();
38970         }
38971         var ds = this.grid.dataSource;
38972         for(var i = 0, len = records.length; i < len; i++){
38973             this.selectRow(ds.indexOf(records[i]), true);
38974         }
38975     },
38976
38977     /**
38978      * Gets the number of selected rows.
38979      * @return {Number}
38980      */
38981     getCount : function(){
38982         return this.selections.length;
38983     },
38984
38985     /**
38986      * Selects the first row in the grid.
38987      */
38988     selectFirstRow : function(){
38989         this.selectRow(0);
38990     },
38991
38992     /**
38993      * Select the last row.
38994      * @param {Boolean} keepExisting (optional) True to keep existing selections
38995      */
38996     selectLastRow : function(keepExisting){
38997         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
38998     },
38999
39000     /**
39001      * Selects the row immediately following the last selected row.
39002      * @param {Boolean} keepExisting (optional) True to keep existing selections
39003      */
39004     selectNext : function(keepExisting){
39005         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39006             this.selectRow(this.last+1, keepExisting);
39007             this.grid.getView().focusRow(this.last);
39008         }
39009     },
39010
39011     /**
39012      * Selects the row that precedes the last selected row.
39013      * @param {Boolean} keepExisting (optional) True to keep existing selections
39014      */
39015     selectPrevious : function(keepExisting){
39016         if(this.last){
39017             this.selectRow(this.last-1, keepExisting);
39018             this.grid.getView().focusRow(this.last);
39019         }
39020     },
39021
39022     /**
39023      * Returns the selected records
39024      * @return {Array} Array of selected records
39025      */
39026     getSelections : function(){
39027         return [].concat(this.selections.items);
39028     },
39029
39030     /**
39031      * Returns the first selected record.
39032      * @return {Record}
39033      */
39034     getSelected : function(){
39035         return this.selections.itemAt(0);
39036     },
39037
39038
39039     /**
39040      * Clears all selections.
39041      */
39042     clearSelections : function(fast){
39043         if(this.locked) return;
39044         if(fast !== true){
39045             var ds = this.grid.dataSource;
39046             var s = this.selections;
39047             s.each(function(r){
39048                 this.deselectRow(ds.indexOfId(r.id));
39049             }, this);
39050             s.clear();
39051         }else{
39052             this.selections.clear();
39053         }
39054         this.last = false;
39055     },
39056
39057
39058     /**
39059      * Selects all rows.
39060      */
39061     selectAll : function(){
39062         if(this.locked) return;
39063         this.selections.clear();
39064         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39065             this.selectRow(i, true);
39066         }
39067     },
39068
39069     /**
39070      * Returns True if there is a selection.
39071      * @return {Boolean}
39072      */
39073     hasSelection : function(){
39074         return this.selections.length > 0;
39075     },
39076
39077     /**
39078      * Returns True if the specified row is selected.
39079      * @param {Number/Record} record The record or index of the record to check
39080      * @return {Boolean}
39081      */
39082     isSelected : function(index){
39083         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39084         return (r && this.selections.key(r.id) ? true : false);
39085     },
39086
39087     /**
39088      * Returns True if the specified record id is selected.
39089      * @param {String} id The id of record to check
39090      * @return {Boolean}
39091      */
39092     isIdSelected : function(id){
39093         return (this.selections.key(id) ? true : false);
39094     },
39095
39096     // private
39097     handleMouseDown : function(e, t){
39098         var view = this.grid.getView(), rowIndex;
39099         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39100             return;
39101         };
39102         if(e.shiftKey && this.last !== false){
39103             var last = this.last;
39104             this.selectRange(last, rowIndex, e.ctrlKey);
39105             this.last = last; // reset the last
39106             view.focusRow(rowIndex);
39107         }else{
39108             var isSelected = this.isSelected(rowIndex);
39109             if(e.button !== 0 && isSelected){
39110                 view.focusRow(rowIndex);
39111             }else if(e.ctrlKey && isSelected){
39112                 this.deselectRow(rowIndex);
39113             }else if(!isSelected){
39114                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39115                 view.focusRow(rowIndex);
39116             }
39117         }
39118         this.fireEvent("afterselectionchange", this);
39119     },
39120     // private
39121     handleDragableRowClick :  function(grid, rowIndex, e) 
39122     {
39123         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39124             this.selectRow(rowIndex, false);
39125             grid.view.focusRow(rowIndex);
39126              this.fireEvent("afterselectionchange", this);
39127         }
39128     },
39129     
39130     /**
39131      * Selects multiple rows.
39132      * @param {Array} rows Array of the indexes of the row to select
39133      * @param {Boolean} keepExisting (optional) True to keep existing selections
39134      */
39135     selectRows : function(rows, keepExisting){
39136         if(!keepExisting){
39137             this.clearSelections();
39138         }
39139         for(var i = 0, len = rows.length; i < len; i++){
39140             this.selectRow(rows[i], true);
39141         }
39142     },
39143
39144     /**
39145      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39146      * @param {Number} startRow The index of the first row in the range
39147      * @param {Number} endRow The index of the last row in the range
39148      * @param {Boolean} keepExisting (optional) True to retain existing selections
39149      */
39150     selectRange : function(startRow, endRow, keepExisting){
39151         if(this.locked) return;
39152         if(!keepExisting){
39153             this.clearSelections();
39154         }
39155         if(startRow <= endRow){
39156             for(var i = startRow; i <= endRow; i++){
39157                 this.selectRow(i, true);
39158             }
39159         }else{
39160             for(var i = startRow; i >= endRow; i--){
39161                 this.selectRow(i, true);
39162             }
39163         }
39164     },
39165
39166     /**
39167      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39168      * @param {Number} startRow The index of the first row in the range
39169      * @param {Number} endRow The index of the last row in the range
39170      */
39171     deselectRange : function(startRow, endRow, preventViewNotify){
39172         if(this.locked) return;
39173         for(var i = startRow; i <= endRow; i++){
39174             this.deselectRow(i, preventViewNotify);
39175         }
39176     },
39177
39178     /**
39179      * Selects a row.
39180      * @param {Number} row The index of the row to select
39181      * @param {Boolean} keepExisting (optional) True to keep existing selections
39182      */
39183     selectRow : function(index, keepExisting, preventViewNotify){
39184         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39185         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39186             if(!keepExisting || this.singleSelect){
39187                 this.clearSelections();
39188             }
39189             var r = this.grid.dataSource.getAt(index);
39190             this.selections.add(r);
39191             this.last = this.lastActive = index;
39192             if(!preventViewNotify){
39193                 this.grid.getView().onRowSelect(index);
39194             }
39195             this.fireEvent("rowselect", this, index, r);
39196             this.fireEvent("selectionchange", this);
39197         }
39198     },
39199
39200     /**
39201      * Deselects a row.
39202      * @param {Number} row The index of the row to deselect
39203      */
39204     deselectRow : function(index, preventViewNotify){
39205         if(this.locked) return;
39206         if(this.last == index){
39207             this.last = false;
39208         }
39209         if(this.lastActive == index){
39210             this.lastActive = false;
39211         }
39212         var r = this.grid.dataSource.getAt(index);
39213         this.selections.remove(r);
39214         if(!preventViewNotify){
39215             this.grid.getView().onRowDeselect(index);
39216         }
39217         this.fireEvent("rowdeselect", this, index);
39218         this.fireEvent("selectionchange", this);
39219     },
39220
39221     // private
39222     restoreLast : function(){
39223         if(this._last){
39224             this.last = this._last;
39225         }
39226     },
39227
39228     // private
39229     acceptsNav : function(row, col, cm){
39230         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39231     },
39232
39233     // private
39234     onEditorKey : function(field, e){
39235         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39236         if(k == e.TAB){
39237             e.stopEvent();
39238             ed.completeEdit();
39239             if(e.shiftKey){
39240                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39241             }else{
39242                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39243             }
39244         }else if(k == e.ENTER && !e.ctrlKey){
39245             e.stopEvent();
39246             ed.completeEdit();
39247             if(e.shiftKey){
39248                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39249             }else{
39250                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39251             }
39252         }else if(k == e.ESC){
39253             ed.cancelEdit();
39254         }
39255         if(newCell){
39256             g.startEditing(newCell[0], newCell[1]);
39257         }
39258     }
39259 });/*
39260  * Based on:
39261  * Ext JS Library 1.1.1
39262  * Copyright(c) 2006-2007, Ext JS, LLC.
39263  *
39264  * Originally Released Under LGPL - original licence link has changed is not relivant.
39265  *
39266  * Fork - LGPL
39267  * <script type="text/javascript">
39268  */
39269 /**
39270  * @class Roo.grid.CellSelectionModel
39271  * @extends Roo.grid.AbstractSelectionModel
39272  * This class provides the basic implementation for cell selection in a grid.
39273  * @constructor
39274  * @param {Object} config The object containing the configuration of this model.
39275  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39276  */
39277 Roo.grid.CellSelectionModel = function(config){
39278     Roo.apply(this, config);
39279
39280     this.selection = null;
39281
39282     this.addEvents({
39283         /**
39284              * @event beforerowselect
39285              * Fires before a cell is selected.
39286              * @param {SelectionModel} this
39287              * @param {Number} rowIndex The selected row index
39288              * @param {Number} colIndex The selected cell index
39289              */
39290             "beforecellselect" : true,
39291         /**
39292              * @event cellselect
39293              * Fires when a cell is selected.
39294              * @param {SelectionModel} this
39295              * @param {Number} rowIndex The selected row index
39296              * @param {Number} colIndex The selected cell index
39297              */
39298             "cellselect" : true,
39299         /**
39300              * @event selectionchange
39301              * Fires when the active selection changes.
39302              * @param {SelectionModel} this
39303              * @param {Object} selection null for no selection or an object (o) with two properties
39304                 <ul>
39305                 <li>o.record: the record object for the row the selection is in</li>
39306                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39307                 </ul>
39308              */
39309             "selectionchange" : true,
39310         /**
39311              * @event tabend
39312              * Fires when the tab (or enter) was pressed on the last editable cell
39313              * You can use this to trigger add new row.
39314              * @param {SelectionModel} this
39315              */
39316             "tabend" : true,
39317          /**
39318              * @event beforeeditnext
39319              * Fires before the next editable sell is made active
39320              * You can use this to skip to another cell or fire the tabend
39321              *    if you set cell to false
39322              * @param {Object} eventdata object : { cell : [ row, col ] } 
39323              */
39324             "beforeeditnext" : true
39325     });
39326     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39327 };
39328
39329 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39330     
39331     enter_is_tab: false,
39332
39333     /** @ignore */
39334     initEvents : function(){
39335         this.grid.on("mousedown", this.handleMouseDown, this);
39336         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39337         var view = this.grid.view;
39338         view.on("refresh", this.onViewChange, this);
39339         view.on("rowupdated", this.onRowUpdated, this);
39340         view.on("beforerowremoved", this.clearSelections, this);
39341         view.on("beforerowsinserted", this.clearSelections, this);
39342         if(this.grid.isEditor){
39343             this.grid.on("beforeedit", this.beforeEdit,  this);
39344         }
39345     },
39346
39347         //private
39348     beforeEdit : function(e){
39349         this.select(e.row, e.column, false, true, e.record);
39350     },
39351
39352         //private
39353     onRowUpdated : function(v, index, r){
39354         if(this.selection && this.selection.record == r){
39355             v.onCellSelect(index, this.selection.cell[1]);
39356         }
39357     },
39358
39359         //private
39360     onViewChange : function(){
39361         this.clearSelections(true);
39362     },
39363
39364         /**
39365          * Returns the currently selected cell,.
39366          * @return {Array} The selected cell (row, column) or null if none selected.
39367          */
39368     getSelectedCell : function(){
39369         return this.selection ? this.selection.cell : null;
39370     },
39371
39372     /**
39373      * Clears all selections.
39374      * @param {Boolean} true to prevent the gridview from being notified about the change.
39375      */
39376     clearSelections : function(preventNotify){
39377         var s = this.selection;
39378         if(s){
39379             if(preventNotify !== true){
39380                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39381             }
39382             this.selection = null;
39383             this.fireEvent("selectionchange", this, null);
39384         }
39385     },
39386
39387     /**
39388      * Returns true if there is a selection.
39389      * @return {Boolean}
39390      */
39391     hasSelection : function(){
39392         return this.selection ? true : false;
39393     },
39394
39395     /** @ignore */
39396     handleMouseDown : function(e, t){
39397         var v = this.grid.getView();
39398         if(this.isLocked()){
39399             return;
39400         };
39401         var row = v.findRowIndex(t);
39402         var cell = v.findCellIndex(t);
39403         if(row !== false && cell !== false){
39404             this.select(row, cell);
39405         }
39406     },
39407
39408     /**
39409      * Selects a cell.
39410      * @param {Number} rowIndex
39411      * @param {Number} collIndex
39412      */
39413     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39414         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39415             this.clearSelections();
39416             r = r || this.grid.dataSource.getAt(rowIndex);
39417             this.selection = {
39418                 record : r,
39419                 cell : [rowIndex, colIndex]
39420             };
39421             if(!preventViewNotify){
39422                 var v = this.grid.getView();
39423                 v.onCellSelect(rowIndex, colIndex);
39424                 if(preventFocus !== true){
39425                     v.focusCell(rowIndex, colIndex);
39426                 }
39427             }
39428             this.fireEvent("cellselect", this, rowIndex, colIndex);
39429             this.fireEvent("selectionchange", this, this.selection);
39430         }
39431     },
39432
39433         //private
39434     isSelectable : function(rowIndex, colIndex, cm){
39435         return !cm.isHidden(colIndex);
39436     },
39437
39438     /** @ignore */
39439     handleKeyDown : function(e){
39440         //Roo.log('Cell Sel Model handleKeyDown');
39441         if(!e.isNavKeyPress()){
39442             return;
39443         }
39444         var g = this.grid, s = this.selection;
39445         if(!s){
39446             e.stopEvent();
39447             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39448             if(cell){
39449                 this.select(cell[0], cell[1]);
39450             }
39451             return;
39452         }
39453         var sm = this;
39454         var walk = function(row, col, step){
39455             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39456         };
39457         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39458         var newCell;
39459
39460       
39461
39462         switch(k){
39463             case e.TAB:
39464                 // handled by onEditorKey
39465                 if (g.isEditor && g.editing) {
39466                     return;
39467                 }
39468                 if(e.shiftKey) {
39469                     newCell = walk(r, c-1, -1);
39470                 } else {
39471                     newCell = walk(r, c+1, 1);
39472                 }
39473                 break;
39474             
39475             case e.DOWN:
39476                newCell = walk(r+1, c, 1);
39477                 break;
39478             
39479             case e.UP:
39480                 newCell = walk(r-1, c, -1);
39481                 break;
39482             
39483             case e.RIGHT:
39484                 newCell = walk(r, c+1, 1);
39485                 break;
39486             
39487             case e.LEFT:
39488                 newCell = walk(r, c-1, -1);
39489                 break;
39490             
39491             case e.ENTER:
39492                 
39493                 if(g.isEditor && !g.editing){
39494                    g.startEditing(r, c);
39495                    e.stopEvent();
39496                    return;
39497                 }
39498                 
39499                 
39500              break;
39501         };
39502         if(newCell){
39503             this.select(newCell[0], newCell[1]);
39504             e.stopEvent();
39505             
39506         }
39507     },
39508
39509     acceptsNav : function(row, col, cm){
39510         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39511     },
39512     /**
39513      * Selects a cell.
39514      * @param {Number} field (not used) - as it's normally used as a listener
39515      * @param {Number} e - event - fake it by using
39516      *
39517      * var e = Roo.EventObjectImpl.prototype;
39518      * e.keyCode = e.TAB
39519      *
39520      * 
39521      */
39522     onEditorKey : function(field, e){
39523         
39524         var k = e.getKey(),
39525             newCell,
39526             g = this.grid,
39527             ed = g.activeEditor,
39528             forward = false;
39529         ///Roo.log('onEditorKey' + k);
39530         
39531         
39532         if (this.enter_is_tab && k == e.ENTER) {
39533             k = e.TAB;
39534         }
39535         
39536         if(k == e.TAB){
39537             if(e.shiftKey){
39538                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39539             }else{
39540                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39541                 forward = true;
39542             }
39543             
39544             e.stopEvent();
39545             
39546         } else if(k == e.ENTER &&  !e.ctrlKey){
39547             ed.completeEdit();
39548             e.stopEvent();
39549             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39550         
39551                 } else if(k == e.ESC){
39552             ed.cancelEdit();
39553         }
39554                 
39555         if (newCell) {
39556             var ecall = { cell : newCell, forward : forward };
39557             this.fireEvent('beforeeditnext', ecall );
39558             newCell = ecall.cell;
39559                         forward = ecall.forward;
39560         }
39561                 
39562         if(newCell){
39563             //Roo.log('next cell after edit');
39564             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39565         } else if (forward) {
39566             // tabbed past last
39567             this.fireEvent.defer(100, this, ['tabend',this]);
39568         }
39569     }
39570 });/*
39571  * Based on:
39572  * Ext JS Library 1.1.1
39573  * Copyright(c) 2006-2007, Ext JS, LLC.
39574  *
39575  * Originally Released Under LGPL - original licence link has changed is not relivant.
39576  *
39577  * Fork - LGPL
39578  * <script type="text/javascript">
39579  */
39580  
39581 /**
39582  * @class Roo.grid.EditorGrid
39583  * @extends Roo.grid.Grid
39584  * Class for creating and editable grid.
39585  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39586  * The container MUST have some type of size defined for the grid to fill. The container will be 
39587  * automatically set to position relative if it isn't already.
39588  * @param {Object} dataSource The data model to bind to
39589  * @param {Object} colModel The column model with info about this grid's columns
39590  */
39591 Roo.grid.EditorGrid = function(container, config){
39592     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39593     this.getGridEl().addClass("xedit-grid");
39594
39595     if(!this.selModel){
39596         this.selModel = new Roo.grid.CellSelectionModel();
39597     }
39598
39599     this.activeEditor = null;
39600
39601         this.addEvents({
39602             /**
39603              * @event beforeedit
39604              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39605              * <ul style="padding:5px;padding-left:16px;">
39606              * <li>grid - This grid</li>
39607              * <li>record - The record being edited</li>
39608              * <li>field - The field name being edited</li>
39609              * <li>value - The value for the field being edited.</li>
39610              * <li>row - The grid row index</li>
39611              * <li>column - The grid column index</li>
39612              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39613              * </ul>
39614              * @param {Object} e An edit event (see above for description)
39615              */
39616             "beforeedit" : true,
39617             /**
39618              * @event afteredit
39619              * Fires after a cell is edited. <br />
39620              * <ul style="padding:5px;padding-left:16px;">
39621              * <li>grid - This grid</li>
39622              * <li>record - The record being edited</li>
39623              * <li>field - The field name being edited</li>
39624              * <li>value - The value being set</li>
39625              * <li>originalValue - The original value for the field, before the edit.</li>
39626              * <li>row - The grid row index</li>
39627              * <li>column - The grid column index</li>
39628              * </ul>
39629              * @param {Object} e An edit event (see above for description)
39630              */
39631             "afteredit" : true,
39632             /**
39633              * @event validateedit
39634              * Fires after a cell is edited, but before the value is set in the record. 
39635          * You can use this to modify the value being set in the field, Return false
39636              * to cancel the change. The edit event object has the following properties <br />
39637              * <ul style="padding:5px;padding-left:16px;">
39638          * <li>editor - This editor</li>
39639              * <li>grid - This grid</li>
39640              * <li>record - The record being edited</li>
39641              * <li>field - The field name being edited</li>
39642              * <li>value - The value being set</li>
39643              * <li>originalValue - The original value for the field, before the edit.</li>
39644              * <li>row - The grid row index</li>
39645              * <li>column - The grid column index</li>
39646              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39647              * </ul>
39648              * @param {Object} e An edit event (see above for description)
39649              */
39650             "validateedit" : true
39651         });
39652     this.on("bodyscroll", this.stopEditing,  this);
39653     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39654 };
39655
39656 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39657     /**
39658      * @cfg {Number} clicksToEdit
39659      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39660      */
39661     clicksToEdit: 2,
39662
39663     // private
39664     isEditor : true,
39665     // private
39666     trackMouseOver: false, // causes very odd FF errors
39667
39668     onCellDblClick : function(g, row, col){
39669         this.startEditing(row, col);
39670     },
39671
39672     onEditComplete : function(ed, value, startValue){
39673         this.editing = false;
39674         this.activeEditor = null;
39675         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39676         var r = ed.record;
39677         var field = this.colModel.getDataIndex(ed.col);
39678         var e = {
39679             grid: this,
39680             record: r,
39681             field: field,
39682             originalValue: startValue,
39683             value: value,
39684             row: ed.row,
39685             column: ed.col,
39686             cancel:false,
39687             editor: ed
39688         };
39689         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39690         cell.show();
39691           
39692         if(String(value) !== String(startValue)){
39693             
39694             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39695                 r.set(field, e.value);
39696                 // if we are dealing with a combo box..
39697                 // then we also set the 'name' colum to be the displayField
39698                 if (ed.field.displayField && ed.field.name) {
39699                     r.set(ed.field.name, ed.field.el.dom.value);
39700                 }
39701                 
39702                 delete e.cancel; //?? why!!!
39703                 this.fireEvent("afteredit", e);
39704             }
39705         } else {
39706             this.fireEvent("afteredit", e); // always fire it!
39707         }
39708         this.view.focusCell(ed.row, ed.col);
39709     },
39710
39711     /**
39712      * Starts editing the specified for the specified row/column
39713      * @param {Number} rowIndex
39714      * @param {Number} colIndex
39715      */
39716     startEditing : function(row, col){
39717         this.stopEditing();
39718         if(this.colModel.isCellEditable(col, row)){
39719             this.view.ensureVisible(row, col, true);
39720           
39721             var r = this.dataSource.getAt(row);
39722             var field = this.colModel.getDataIndex(col);
39723             var cell = Roo.get(this.view.getCell(row,col));
39724             var e = {
39725                 grid: this,
39726                 record: r,
39727                 field: field,
39728                 value: r.data[field],
39729                 row: row,
39730                 column: col,
39731                 cancel:false 
39732             };
39733             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39734                 this.editing = true;
39735                 var ed = this.colModel.getCellEditor(col, row);
39736                 
39737                 if (!ed) {
39738                     return;
39739                 }
39740                 if(!ed.rendered){
39741                     ed.render(ed.parentEl || document.body);
39742                 }
39743                 ed.field.reset();
39744                
39745                 cell.hide();
39746                 
39747                 (function(){ // complex but required for focus issues in safari, ie and opera
39748                     ed.row = row;
39749                     ed.col = col;
39750                     ed.record = r;
39751                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39752                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39753                     this.activeEditor = ed;
39754                     var v = r.data[field];
39755                     ed.startEdit(this.view.getCell(row, col), v);
39756                     // combo's with 'displayField and name set
39757                     if (ed.field.displayField && ed.field.name) {
39758                         ed.field.el.dom.value = r.data[ed.field.name];
39759                     }
39760                     
39761                     
39762                 }).defer(50, this);
39763             }
39764         }
39765     },
39766         
39767     /**
39768      * Stops any active editing
39769      */
39770     stopEditing : function(){
39771         if(this.activeEditor){
39772             this.activeEditor.completeEdit();
39773         }
39774         this.activeEditor = null;
39775     },
39776         
39777          /**
39778      * Called to get grid's drag proxy text, by default returns this.ddText.
39779      * @return {String}
39780      */
39781     getDragDropText : function(){
39782         var count = this.selModel.getSelectedCell() ? 1 : 0;
39783         return String.format(this.ddText, count, count == 1 ? '' : 's');
39784     }
39785         
39786 });/*
39787  * Based on:
39788  * Ext JS Library 1.1.1
39789  * Copyright(c) 2006-2007, Ext JS, LLC.
39790  *
39791  * Originally Released Under LGPL - original licence link has changed is not relivant.
39792  *
39793  * Fork - LGPL
39794  * <script type="text/javascript">
39795  */
39796
39797 // private - not really -- you end up using it !
39798 // This is a support class used internally by the Grid components
39799
39800 /**
39801  * @class Roo.grid.GridEditor
39802  * @extends Roo.Editor
39803  * Class for creating and editable grid elements.
39804  * @param {Object} config any settings (must include field)
39805  */
39806 Roo.grid.GridEditor = function(field, config){
39807     if (!config && field.field) {
39808         config = field;
39809         field = Roo.factory(config.field, Roo.form);
39810     }
39811     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39812     field.monitorTab = false;
39813 };
39814
39815 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39816     
39817     /**
39818      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39819      */
39820     
39821     alignment: "tl-tl",
39822     autoSize: "width",
39823     hideEl : false,
39824     cls: "x-small-editor x-grid-editor",
39825     shim:false,
39826     shadow:"frame"
39827 });/*
39828  * Based on:
39829  * Ext JS Library 1.1.1
39830  * Copyright(c) 2006-2007, Ext JS, LLC.
39831  *
39832  * Originally Released Under LGPL - original licence link has changed is not relivant.
39833  *
39834  * Fork - LGPL
39835  * <script type="text/javascript">
39836  */
39837   
39838
39839   
39840 Roo.grid.PropertyRecord = Roo.data.Record.create([
39841     {name:'name',type:'string'},  'value'
39842 ]);
39843
39844
39845 Roo.grid.PropertyStore = function(grid, source){
39846     this.grid = grid;
39847     this.store = new Roo.data.Store({
39848         recordType : Roo.grid.PropertyRecord
39849     });
39850     this.store.on('update', this.onUpdate,  this);
39851     if(source){
39852         this.setSource(source);
39853     }
39854     Roo.grid.PropertyStore.superclass.constructor.call(this);
39855 };
39856
39857
39858
39859 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39860     setSource : function(o){
39861         this.source = o;
39862         this.store.removeAll();
39863         var data = [];
39864         for(var k in o){
39865             if(this.isEditableValue(o[k])){
39866                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39867             }
39868         }
39869         this.store.loadRecords({records: data}, {}, true);
39870     },
39871
39872     onUpdate : function(ds, record, type){
39873         if(type == Roo.data.Record.EDIT){
39874             var v = record.data['value'];
39875             var oldValue = record.modified['value'];
39876             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39877                 this.source[record.id] = v;
39878                 record.commit();
39879                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39880             }else{
39881                 record.reject();
39882             }
39883         }
39884     },
39885
39886     getProperty : function(row){
39887        return this.store.getAt(row);
39888     },
39889
39890     isEditableValue: function(val){
39891         if(val && val instanceof Date){
39892             return true;
39893         }else if(typeof val == 'object' || typeof val == 'function'){
39894             return false;
39895         }
39896         return true;
39897     },
39898
39899     setValue : function(prop, value){
39900         this.source[prop] = value;
39901         this.store.getById(prop).set('value', value);
39902     },
39903
39904     getSource : function(){
39905         return this.source;
39906     }
39907 });
39908
39909 Roo.grid.PropertyColumnModel = function(grid, store){
39910     this.grid = grid;
39911     var g = Roo.grid;
39912     g.PropertyColumnModel.superclass.constructor.call(this, [
39913         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39914         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39915     ]);
39916     this.store = store;
39917     this.bselect = Roo.DomHelper.append(document.body, {
39918         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39919             {tag: 'option', value: 'true', html: 'true'},
39920             {tag: 'option', value: 'false', html: 'false'}
39921         ]
39922     });
39923     Roo.id(this.bselect);
39924     var f = Roo.form;
39925     this.editors = {
39926         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39927         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39928         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39929         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39930         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39931     };
39932     this.renderCellDelegate = this.renderCell.createDelegate(this);
39933     this.renderPropDelegate = this.renderProp.createDelegate(this);
39934 };
39935
39936 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39937     
39938     
39939     nameText : 'Name',
39940     valueText : 'Value',
39941     
39942     dateFormat : 'm/j/Y',
39943     
39944     
39945     renderDate : function(dateVal){
39946         return dateVal.dateFormat(this.dateFormat);
39947     },
39948
39949     renderBool : function(bVal){
39950         return bVal ? 'true' : 'false';
39951     },
39952
39953     isCellEditable : function(colIndex, rowIndex){
39954         return colIndex == 1;
39955     },
39956
39957     getRenderer : function(col){
39958         return col == 1 ?
39959             this.renderCellDelegate : this.renderPropDelegate;
39960     },
39961
39962     renderProp : function(v){
39963         return this.getPropertyName(v);
39964     },
39965
39966     renderCell : function(val){
39967         var rv = val;
39968         if(val instanceof Date){
39969             rv = this.renderDate(val);
39970         }else if(typeof val == 'boolean'){
39971             rv = this.renderBool(val);
39972         }
39973         return Roo.util.Format.htmlEncode(rv);
39974     },
39975
39976     getPropertyName : function(name){
39977         var pn = this.grid.propertyNames;
39978         return pn && pn[name] ? pn[name] : name;
39979     },
39980
39981     getCellEditor : function(colIndex, rowIndex){
39982         var p = this.store.getProperty(rowIndex);
39983         var n = p.data['name'], val = p.data['value'];
39984         
39985         if(typeof(this.grid.customEditors[n]) == 'string'){
39986             return this.editors[this.grid.customEditors[n]];
39987         }
39988         if(typeof(this.grid.customEditors[n]) != 'undefined'){
39989             return this.grid.customEditors[n];
39990         }
39991         if(val instanceof Date){
39992             return this.editors['date'];
39993         }else if(typeof val == 'number'){
39994             return this.editors['number'];
39995         }else if(typeof val == 'boolean'){
39996             return this.editors['boolean'];
39997         }else{
39998             return this.editors['string'];
39999         }
40000     }
40001 });
40002
40003 /**
40004  * @class Roo.grid.PropertyGrid
40005  * @extends Roo.grid.EditorGrid
40006  * This class represents the  interface of a component based property grid control.
40007  * <br><br>Usage:<pre><code>
40008  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40009       
40010  });
40011  // set any options
40012  grid.render();
40013  * </code></pre>
40014   
40015  * @constructor
40016  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40017  * The container MUST have some type of size defined for the grid to fill. The container will be
40018  * automatically set to position relative if it isn't already.
40019  * @param {Object} config A config object that sets properties on this grid.
40020  */
40021 Roo.grid.PropertyGrid = function(container, config){
40022     config = config || {};
40023     var store = new Roo.grid.PropertyStore(this);
40024     this.store = store;
40025     var cm = new Roo.grid.PropertyColumnModel(this, store);
40026     store.store.sort('name', 'ASC');
40027     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40028         ds: store.store,
40029         cm: cm,
40030         enableColLock:false,
40031         enableColumnMove:false,
40032         stripeRows:false,
40033         trackMouseOver: false,
40034         clicksToEdit:1
40035     }, config));
40036     this.getGridEl().addClass('x-props-grid');
40037     this.lastEditRow = null;
40038     this.on('columnresize', this.onColumnResize, this);
40039     this.addEvents({
40040          /**
40041              * @event beforepropertychange
40042              * Fires before a property changes (return false to stop?)
40043              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40044              * @param {String} id Record Id
40045              * @param {String} newval New Value
40046          * @param {String} oldval Old Value
40047              */
40048         "beforepropertychange": true,
40049         /**
40050              * @event propertychange
40051              * Fires after a property changes
40052              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40053              * @param {String} id Record Id
40054              * @param {String} newval New Value
40055          * @param {String} oldval Old Value
40056              */
40057         "propertychange": true
40058     });
40059     this.customEditors = this.customEditors || {};
40060 };
40061 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40062     
40063      /**
40064      * @cfg {Object} customEditors map of colnames=> custom editors.
40065      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40066      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40067      * false disables editing of the field.
40068          */
40069     
40070       /**
40071      * @cfg {Object} propertyNames map of property Names to their displayed value
40072          */
40073     
40074     render : function(){
40075         Roo.grid.PropertyGrid.superclass.render.call(this);
40076         this.autoSize.defer(100, this);
40077     },
40078
40079     autoSize : function(){
40080         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40081         if(this.view){
40082             this.view.fitColumns();
40083         }
40084     },
40085
40086     onColumnResize : function(){
40087         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40088         this.autoSize();
40089     },
40090     /**
40091      * Sets the data for the Grid
40092      * accepts a Key => Value object of all the elements avaiable.
40093      * @param {Object} data  to appear in grid.
40094      */
40095     setSource : function(source){
40096         this.store.setSource(source);
40097         //this.autoSize();
40098     },
40099     /**
40100      * Gets all the data from the grid.
40101      * @return {Object} data  data stored in grid
40102      */
40103     getSource : function(){
40104         return this.store.getSource();
40105     }
40106 });/*
40107   
40108  * Licence LGPL
40109  
40110  */
40111  
40112 /**
40113  * @class Roo.grid.Calendar
40114  * @extends Roo.util.Grid
40115  * This class extends the Grid to provide a calendar widget
40116  * <br><br>Usage:<pre><code>
40117  var grid = new Roo.grid.Calendar("my-container-id", {
40118      ds: myDataStore,
40119      cm: myColModel,
40120      selModel: mySelectionModel,
40121      autoSizeColumns: true,
40122      monitorWindowResize: false,
40123      trackMouseOver: true
40124      eventstore : real data store..
40125  });
40126  // set any options
40127  grid.render();
40128   
40129   * @constructor
40130  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40131  * The container MUST have some type of size defined for the grid to fill. The container will be
40132  * automatically set to position relative if it isn't already.
40133  * @param {Object} config A config object that sets properties on this grid.
40134  */
40135 Roo.grid.Calendar = function(container, config){
40136         // initialize the container
40137         this.container = Roo.get(container);
40138         this.container.update("");
40139         this.container.setStyle("overflow", "hidden");
40140     this.container.addClass('x-grid-container');
40141
40142     this.id = this.container.id;
40143
40144     Roo.apply(this, config);
40145     // check and correct shorthanded configs
40146     
40147     var rows = [];
40148     var d =1;
40149     for (var r = 0;r < 6;r++) {
40150         
40151         rows[r]=[];
40152         for (var c =0;c < 7;c++) {
40153             rows[r][c]= '';
40154         }
40155     }
40156     if (this.eventStore) {
40157         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40158         this.eventStore.on('load',this.onLoad, this);
40159         this.eventStore.on('beforeload',this.clearEvents, this);
40160          
40161     }
40162     
40163     this.dataSource = new Roo.data.Store({
40164             proxy: new Roo.data.MemoryProxy(rows),
40165             reader: new Roo.data.ArrayReader({}, [
40166                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40167     });
40168
40169     this.dataSource.load();
40170     this.ds = this.dataSource;
40171     this.ds.xmodule = this.xmodule || false;
40172     
40173     
40174     var cellRender = function(v,x,r)
40175     {
40176         return String.format(
40177             '<div class="fc-day  fc-widget-content"><div>' +
40178                 '<div class="fc-event-container"></div>' +
40179                 '<div class="fc-day-number">{0}</div>'+
40180                 
40181                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40182             '</div></div>', v);
40183     
40184     }
40185     
40186     
40187     this.colModel = new Roo.grid.ColumnModel( [
40188         {
40189             xtype: 'ColumnModel',
40190             xns: Roo.grid,
40191             dataIndex : 'weekday0',
40192             header : 'Sunday',
40193             renderer : cellRender
40194         },
40195         {
40196             xtype: 'ColumnModel',
40197             xns: Roo.grid,
40198             dataIndex : 'weekday1',
40199             header : 'Monday',
40200             renderer : cellRender
40201         },
40202         {
40203             xtype: 'ColumnModel',
40204             xns: Roo.grid,
40205             dataIndex : 'weekday2',
40206             header : 'Tuesday',
40207             renderer : cellRender
40208         },
40209         {
40210             xtype: 'ColumnModel',
40211             xns: Roo.grid,
40212             dataIndex : 'weekday3',
40213             header : 'Wednesday',
40214             renderer : cellRender
40215         },
40216         {
40217             xtype: 'ColumnModel',
40218             xns: Roo.grid,
40219             dataIndex : 'weekday4',
40220             header : 'Thursday',
40221             renderer : cellRender
40222         },
40223         {
40224             xtype: 'ColumnModel',
40225             xns: Roo.grid,
40226             dataIndex : 'weekday5',
40227             header : 'Friday',
40228             renderer : cellRender
40229         },
40230         {
40231             xtype: 'ColumnModel',
40232             xns: Roo.grid,
40233             dataIndex : 'weekday6',
40234             header : 'Saturday',
40235             renderer : cellRender
40236         }
40237     ]);
40238     this.cm = this.colModel;
40239     this.cm.xmodule = this.xmodule || false;
40240  
40241         
40242           
40243     //this.selModel = new Roo.grid.CellSelectionModel();
40244     //this.sm = this.selModel;
40245     //this.selModel.init(this);
40246     
40247     
40248     if(this.width){
40249         this.container.setWidth(this.width);
40250     }
40251
40252     if(this.height){
40253         this.container.setHeight(this.height);
40254     }
40255     /** @private */
40256         this.addEvents({
40257         // raw events
40258         /**
40259          * @event click
40260          * The raw click event for the entire grid.
40261          * @param {Roo.EventObject} e
40262          */
40263         "click" : true,
40264         /**
40265          * @event dblclick
40266          * The raw dblclick event for the entire grid.
40267          * @param {Roo.EventObject} e
40268          */
40269         "dblclick" : true,
40270         /**
40271          * @event contextmenu
40272          * The raw contextmenu event for the entire grid.
40273          * @param {Roo.EventObject} e
40274          */
40275         "contextmenu" : true,
40276         /**
40277          * @event mousedown
40278          * The raw mousedown event for the entire grid.
40279          * @param {Roo.EventObject} e
40280          */
40281         "mousedown" : true,
40282         /**
40283          * @event mouseup
40284          * The raw mouseup event for the entire grid.
40285          * @param {Roo.EventObject} e
40286          */
40287         "mouseup" : true,
40288         /**
40289          * @event mouseover
40290          * The raw mouseover event for the entire grid.
40291          * @param {Roo.EventObject} e
40292          */
40293         "mouseover" : true,
40294         /**
40295          * @event mouseout
40296          * The raw mouseout event for the entire grid.
40297          * @param {Roo.EventObject} e
40298          */
40299         "mouseout" : true,
40300         /**
40301          * @event keypress
40302          * The raw keypress event for the entire grid.
40303          * @param {Roo.EventObject} e
40304          */
40305         "keypress" : true,
40306         /**
40307          * @event keydown
40308          * The raw keydown event for the entire grid.
40309          * @param {Roo.EventObject} e
40310          */
40311         "keydown" : true,
40312
40313         // custom events
40314
40315         /**
40316          * @event cellclick
40317          * Fires when a cell is clicked
40318          * @param {Grid} this
40319          * @param {Number} rowIndex
40320          * @param {Number} columnIndex
40321          * @param {Roo.EventObject} e
40322          */
40323         "cellclick" : true,
40324         /**
40325          * @event celldblclick
40326          * Fires when a cell is double clicked
40327          * @param {Grid} this
40328          * @param {Number} rowIndex
40329          * @param {Number} columnIndex
40330          * @param {Roo.EventObject} e
40331          */
40332         "celldblclick" : true,
40333         /**
40334          * @event rowclick
40335          * Fires when a row is clicked
40336          * @param {Grid} this
40337          * @param {Number} rowIndex
40338          * @param {Roo.EventObject} e
40339          */
40340         "rowclick" : true,
40341         /**
40342          * @event rowdblclick
40343          * Fires when a row is double clicked
40344          * @param {Grid} this
40345          * @param {Number} rowIndex
40346          * @param {Roo.EventObject} e
40347          */
40348         "rowdblclick" : true,
40349         /**
40350          * @event headerclick
40351          * Fires when a header is clicked
40352          * @param {Grid} this
40353          * @param {Number} columnIndex
40354          * @param {Roo.EventObject} e
40355          */
40356         "headerclick" : true,
40357         /**
40358          * @event headerdblclick
40359          * Fires when a header cell is double clicked
40360          * @param {Grid} this
40361          * @param {Number} columnIndex
40362          * @param {Roo.EventObject} e
40363          */
40364         "headerdblclick" : true,
40365         /**
40366          * @event rowcontextmenu
40367          * Fires when a row is right clicked
40368          * @param {Grid} this
40369          * @param {Number} rowIndex
40370          * @param {Roo.EventObject} e
40371          */
40372         "rowcontextmenu" : true,
40373         /**
40374          * @event cellcontextmenu
40375          * Fires when a cell is right clicked
40376          * @param {Grid} this
40377          * @param {Number} rowIndex
40378          * @param {Number} cellIndex
40379          * @param {Roo.EventObject} e
40380          */
40381          "cellcontextmenu" : true,
40382         /**
40383          * @event headercontextmenu
40384          * Fires when a header is right clicked
40385          * @param {Grid} this
40386          * @param {Number} columnIndex
40387          * @param {Roo.EventObject} e
40388          */
40389         "headercontextmenu" : true,
40390         /**
40391          * @event bodyscroll
40392          * Fires when the body element is scrolled
40393          * @param {Number} scrollLeft
40394          * @param {Number} scrollTop
40395          */
40396         "bodyscroll" : true,
40397         /**
40398          * @event columnresize
40399          * Fires when the user resizes a column
40400          * @param {Number} columnIndex
40401          * @param {Number} newSize
40402          */
40403         "columnresize" : true,
40404         /**
40405          * @event columnmove
40406          * Fires when the user moves a column
40407          * @param {Number} oldIndex
40408          * @param {Number} newIndex
40409          */
40410         "columnmove" : true,
40411         /**
40412          * @event startdrag
40413          * Fires when row(s) start being dragged
40414          * @param {Grid} this
40415          * @param {Roo.GridDD} dd The drag drop object
40416          * @param {event} e The raw browser event
40417          */
40418         "startdrag" : true,
40419         /**
40420          * @event enddrag
40421          * Fires when a drag operation is complete
40422          * @param {Grid} this
40423          * @param {Roo.GridDD} dd The drag drop object
40424          * @param {event} e The raw browser event
40425          */
40426         "enddrag" : true,
40427         /**
40428          * @event dragdrop
40429          * Fires when dragged row(s) are dropped on a valid DD target
40430          * @param {Grid} this
40431          * @param {Roo.GridDD} dd The drag drop object
40432          * @param {String} targetId The target drag drop object
40433          * @param {event} e The raw browser event
40434          */
40435         "dragdrop" : true,
40436         /**
40437          * @event dragover
40438          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40439          * @param {Grid} this
40440          * @param {Roo.GridDD} dd The drag drop object
40441          * @param {String} targetId The target drag drop object
40442          * @param {event} e The raw browser event
40443          */
40444         "dragover" : true,
40445         /**
40446          * @event dragenter
40447          *  Fires when the dragged row(s) first cross another DD target while being dragged
40448          * @param {Grid} this
40449          * @param {Roo.GridDD} dd The drag drop object
40450          * @param {String} targetId The target drag drop object
40451          * @param {event} e The raw browser event
40452          */
40453         "dragenter" : true,
40454         /**
40455          * @event dragout
40456          * Fires when the dragged row(s) leave another DD target while being dragged
40457          * @param {Grid} this
40458          * @param {Roo.GridDD} dd The drag drop object
40459          * @param {String} targetId The target drag drop object
40460          * @param {event} e The raw browser event
40461          */
40462         "dragout" : true,
40463         /**
40464          * @event rowclass
40465          * Fires when a row is rendered, so you can change add a style to it.
40466          * @param {GridView} gridview   The grid view
40467          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
40468          */
40469         'rowclass' : true,
40470
40471         /**
40472          * @event render
40473          * Fires when the grid is rendered
40474          * @param {Grid} grid
40475          */
40476         'render' : true,
40477             /**
40478              * @event select
40479              * Fires when a date is selected
40480              * @param {DatePicker} this
40481              * @param {Date} date The selected date
40482              */
40483         'select': true,
40484         /**
40485              * @event monthchange
40486              * Fires when the displayed month changes 
40487              * @param {DatePicker} this
40488              * @param {Date} date The selected month
40489              */
40490         'monthchange': true,
40491         /**
40492              * @event evententer
40493              * Fires when mouse over an event
40494              * @param {Calendar} this
40495              * @param {event} Event
40496              */
40497         'evententer': true,
40498         /**
40499              * @event eventleave
40500              * Fires when the mouse leaves an
40501              * @param {Calendar} this
40502              * @param {event}
40503              */
40504         'eventleave': true,
40505         /**
40506              * @event eventclick
40507              * Fires when the mouse click an
40508              * @param {Calendar} this
40509              * @param {event}
40510              */
40511         'eventclick': true,
40512         /**
40513              * @event eventrender
40514              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
40515              * @param {Calendar} this
40516              * @param {data} data to be modified
40517              */
40518         'eventrender': true
40519         
40520     });
40521
40522     Roo.grid.Grid.superclass.constructor.call(this);
40523     this.on('render', function() {
40524         this.view.el.addClass('x-grid-cal'); 
40525         
40526         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
40527
40528     },this);
40529     
40530     if (!Roo.grid.Calendar.style) {
40531         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
40532             
40533             
40534             '.x-grid-cal .x-grid-col' :  {
40535                 height: 'auto !important',
40536                 'vertical-align': 'top'
40537             },
40538             '.x-grid-cal  .fc-event-hori' : {
40539                 height: '14px'
40540             }
40541              
40542             
40543         }, Roo.id());
40544     }
40545
40546     
40547     
40548 };
40549 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
40550     /**
40551      * @cfg {Store} eventStore The store that loads events.
40552      */
40553     eventStore : 25,
40554
40555      
40556     activeDate : false,
40557     startDay : 0,
40558     autoWidth : true,
40559     monitorWindowResize : false,
40560
40561     
40562     resizeColumns : function() {
40563         var col = (this.view.el.getWidth() / 7) - 3;
40564         // loop through cols, and setWidth
40565         for(var i =0 ; i < 7 ; i++){
40566             this.cm.setColumnWidth(i, col);
40567         }
40568     },
40569      setDate :function(date) {
40570         
40571         Roo.log('setDate?');
40572         
40573         this.resizeColumns();
40574         var vd = this.activeDate;
40575         this.activeDate = date;
40576 //        if(vd && this.el){
40577 //            var t = date.getTime();
40578 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
40579 //                Roo.log('using add remove');
40580 //                
40581 //                this.fireEvent('monthchange', this, date);
40582 //                
40583 //                this.cells.removeClass("fc-state-highlight");
40584 //                this.cells.each(function(c){
40585 //                   if(c.dateValue == t){
40586 //                       c.addClass("fc-state-highlight");
40587 //                       setTimeout(function(){
40588 //                            try{c.dom.firstChild.focus();}catch(e){}
40589 //                       }, 50);
40590 //                       return false;
40591 //                   }
40592 //                   return true;
40593 //                });
40594 //                return;
40595 //            }
40596 //        }
40597         
40598         var days = date.getDaysInMonth();
40599         
40600         var firstOfMonth = date.getFirstDateOfMonth();
40601         var startingPos = firstOfMonth.getDay()-this.startDay;
40602         
40603         if(startingPos < this.startDay){
40604             startingPos += 7;
40605         }
40606         
40607         var pm = date.add(Date.MONTH, -1);
40608         var prevStart = pm.getDaysInMonth()-startingPos;
40609 //        
40610         
40611         
40612         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40613         
40614         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
40615         //this.cells.addClassOnOver('fc-state-hover');
40616         
40617         var cells = this.cells.elements;
40618         var textEls = this.textNodes;
40619         
40620         //Roo.each(cells, function(cell){
40621         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
40622         //});
40623         
40624         days += startingPos;
40625
40626         // convert everything to numbers so it's fast
40627         var day = 86400000;
40628         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
40629         //Roo.log(d);
40630         //Roo.log(pm);
40631         //Roo.log(prevStart);
40632         
40633         var today = new Date().clearTime().getTime();
40634         var sel = date.clearTime().getTime();
40635         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
40636         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
40637         var ddMatch = this.disabledDatesRE;
40638         var ddText = this.disabledDatesText;
40639         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
40640         var ddaysText = this.disabledDaysText;
40641         var format = this.format;
40642         
40643         var setCellClass = function(cal, cell){
40644             
40645             //Roo.log('set Cell Class');
40646             cell.title = "";
40647             var t = d.getTime();
40648             
40649             //Roo.log(d);
40650             
40651             
40652             cell.dateValue = t;
40653             if(t == today){
40654                 cell.className += " fc-today";
40655                 cell.className += " fc-state-highlight";
40656                 cell.title = cal.todayText;
40657             }
40658             if(t == sel){
40659                 // disable highlight in other month..
40660                 cell.className += " fc-state-highlight";
40661                 
40662             }
40663             // disabling
40664             if(t < min) {
40665                 //cell.className = " fc-state-disabled";
40666                 cell.title = cal.minText;
40667                 return;
40668             }
40669             if(t > max) {
40670                 //cell.className = " fc-state-disabled";
40671                 cell.title = cal.maxText;
40672                 return;
40673             }
40674             if(ddays){
40675                 if(ddays.indexOf(d.getDay()) != -1){
40676                     // cell.title = ddaysText;
40677                    // cell.className = " fc-state-disabled";
40678                 }
40679             }
40680             if(ddMatch && format){
40681                 var fvalue = d.dateFormat(format);
40682                 if(ddMatch.test(fvalue)){
40683                     cell.title = ddText.replace("%0", fvalue);
40684                    cell.className = " fc-state-disabled";
40685                 }
40686             }
40687             
40688             if (!cell.initialClassName) {
40689                 cell.initialClassName = cell.dom.className;
40690             }
40691             
40692             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
40693         };
40694
40695         var i = 0;
40696         
40697         for(; i < startingPos; i++) {
40698             cells[i].dayName =  (++prevStart);
40699             Roo.log(textEls[i]);
40700             d.setDate(d.getDate()+1);
40701             
40702             //cells[i].className = "fc-past fc-other-month";
40703             setCellClass(this, cells[i]);
40704         }
40705         
40706         var intDay = 0;
40707         
40708         for(; i < days; i++){
40709             intDay = i - startingPos + 1;
40710             cells[i].dayName =  (intDay);
40711             d.setDate(d.getDate()+1);
40712             
40713             cells[i].className = ''; // "x-date-active";
40714             setCellClass(this, cells[i]);
40715         }
40716         var extraDays = 0;
40717         
40718         for(; i < 42; i++) {
40719             //textEls[i].innerHTML = (++extraDays);
40720             
40721             d.setDate(d.getDate()+1);
40722             cells[i].dayName = (++extraDays);
40723             cells[i].className = "fc-future fc-other-month";
40724             setCellClass(this, cells[i]);
40725         }
40726         
40727         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
40728         
40729         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
40730         
40731         // this will cause all the cells to mis
40732         var rows= [];
40733         var i =0;
40734         for (var r = 0;r < 6;r++) {
40735             for (var c =0;c < 7;c++) {
40736                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
40737             }    
40738         }
40739         
40740         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40741         for(i=0;i<cells.length;i++) {
40742             
40743             this.cells.elements[i].dayName = cells[i].dayName ;
40744             this.cells.elements[i].className = cells[i].className;
40745             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
40746             this.cells.elements[i].title = cells[i].title ;
40747             this.cells.elements[i].dateValue = cells[i].dateValue ;
40748         }
40749         
40750         
40751         
40752         
40753         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
40754         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
40755         
40756         ////if(totalRows != 6){
40757             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
40758            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
40759        // }
40760         
40761         this.fireEvent('monthchange', this, date);
40762         
40763         
40764     },
40765  /**
40766      * Returns the grid's SelectionModel.
40767      * @return {SelectionModel}
40768      */
40769     getSelectionModel : function(){
40770         if(!this.selModel){
40771             this.selModel = new Roo.grid.CellSelectionModel();
40772         }
40773         return this.selModel;
40774     },
40775
40776     load: function() {
40777         this.eventStore.load()
40778         
40779         
40780         
40781     },
40782     
40783     findCell : function(dt) {
40784         dt = dt.clearTime().getTime();
40785         var ret = false;
40786         this.cells.each(function(c){
40787             //Roo.log("check " +c.dateValue + '?=' + dt);
40788             if(c.dateValue == dt){
40789                 ret = c;
40790                 return false;
40791             }
40792             return true;
40793         });
40794         
40795         return ret;
40796     },
40797     
40798     findCells : function(rec) {
40799         var s = rec.data.start_dt.clone().clearTime().getTime();
40800        // Roo.log(s);
40801         var e= rec.data.end_dt.clone().clearTime().getTime();
40802        // Roo.log(e);
40803         var ret = [];
40804         this.cells.each(function(c){
40805              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
40806             
40807             if(c.dateValue > e){
40808                 return ;
40809             }
40810             if(c.dateValue < s){
40811                 return ;
40812             }
40813             ret.push(c);
40814         });
40815         
40816         return ret;    
40817     },
40818     
40819     findBestRow: function(cells)
40820     {
40821         var ret = 0;
40822         
40823         for (var i =0 ; i < cells.length;i++) {
40824             ret  = Math.max(cells[i].rows || 0,ret);
40825         }
40826         return ret;
40827         
40828     },
40829     
40830     
40831     addItem : function(rec)
40832     {
40833         // look for vertical location slot in
40834         var cells = this.findCells(rec);
40835         
40836         rec.row = this.findBestRow(cells);
40837         
40838         // work out the location.
40839         
40840         var crow = false;
40841         var rows = [];
40842         for(var i =0; i < cells.length; i++) {
40843             if (!crow) {
40844                 crow = {
40845                     start : cells[i],
40846                     end :  cells[i]
40847                 };
40848                 continue;
40849             }
40850             if (crow.start.getY() == cells[i].getY()) {
40851                 // on same row.
40852                 crow.end = cells[i];
40853                 continue;
40854             }
40855             // different row.
40856             rows.push(crow);
40857             crow = {
40858                 start: cells[i],
40859                 end : cells[i]
40860             };
40861             
40862         }
40863         
40864         rows.push(crow);
40865         rec.els = [];
40866         rec.rows = rows;
40867         rec.cells = cells;
40868         for (var i = 0; i < cells.length;i++) {
40869             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
40870             
40871         }
40872         
40873         
40874     },
40875     
40876     clearEvents: function() {
40877         
40878         if (!this.eventStore.getCount()) {
40879             return;
40880         }
40881         // reset number of rows in cells.
40882         Roo.each(this.cells.elements, function(c){
40883             c.rows = 0;
40884         });
40885         
40886         this.eventStore.each(function(e) {
40887             this.clearEvent(e);
40888         },this);
40889         
40890     },
40891     
40892     clearEvent : function(ev)
40893     {
40894         if (ev.els) {
40895             Roo.each(ev.els, function(el) {
40896                 el.un('mouseenter' ,this.onEventEnter, this);
40897                 el.un('mouseleave' ,this.onEventLeave, this);
40898                 el.remove();
40899             },this);
40900             ev.els = [];
40901         }
40902     },
40903     
40904     
40905     renderEvent : function(ev,ctr) {
40906         if (!ctr) {
40907              ctr = this.view.el.select('.fc-event-container',true).first();
40908         }
40909         
40910          
40911         this.clearEvent(ev);
40912             //code
40913        
40914         
40915         
40916         ev.els = [];
40917         var cells = ev.cells;
40918         var rows = ev.rows;
40919         this.fireEvent('eventrender', this, ev);
40920         
40921         for(var i =0; i < rows.length; i++) {
40922             
40923             cls = '';
40924             if (i == 0) {
40925                 cls += ' fc-event-start';
40926             }
40927             if ((i+1) == rows.length) {
40928                 cls += ' fc-event-end';
40929             }
40930             
40931             //Roo.log(ev.data);
40932             // how many rows should it span..
40933             var cg = this.eventTmpl.append(ctr,Roo.apply({
40934                 fccls : cls
40935                 
40936             }, ev.data) , true);
40937             
40938             
40939             cg.on('mouseenter' ,this.onEventEnter, this, ev);
40940             cg.on('mouseleave' ,this.onEventLeave, this, ev);
40941             cg.on('click', this.onEventClick, this, ev);
40942             
40943             ev.els.push(cg);
40944             
40945             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
40946             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
40947             //Roo.log(cg);
40948              
40949             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
40950             cg.setWidth(ebox.right - sbox.x -2);
40951         }
40952     },
40953     
40954     renderEvents: function()
40955     {   
40956         // first make sure there is enough space..
40957         
40958         if (!this.eventTmpl) {
40959             this.eventTmpl = new Roo.Template(
40960                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
40961                     '<div class="fc-event-inner">' +
40962                         '<span class="fc-event-time">{time}</span>' +
40963                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
40964                     '</div>' +
40965                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
40966                 '</div>'
40967             );
40968                 
40969         }
40970                
40971         
40972         
40973         this.cells.each(function(c) {
40974             //Roo.log(c.select('.fc-day-content div',true).first());
40975             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
40976         });
40977         
40978         var ctr = this.view.el.select('.fc-event-container',true).first();
40979         
40980         var cls;
40981         this.eventStore.each(function(ev){
40982             
40983             this.renderEvent(ev);
40984              
40985              
40986         }, this);
40987         this.view.layout();
40988         
40989     },
40990     
40991     onEventEnter: function (e, el,event,d) {
40992         this.fireEvent('evententer', this, el, event);
40993     },
40994     
40995     onEventLeave: function (e, el,event,d) {
40996         this.fireEvent('eventleave', this, el, event);
40997     },
40998     
40999     onEventClick: function (e, el,event,d) {
41000         this.fireEvent('eventclick', this, el, event);
41001     },
41002     
41003     onMonthChange: function () {
41004         this.store.load();
41005     },
41006     
41007     onLoad: function () {
41008         
41009         //Roo.log('calendar onload');
41010 //         
41011         if(this.eventStore.getCount() > 0){
41012             
41013            
41014             
41015             this.eventStore.each(function(d){
41016                 
41017                 
41018                 // FIXME..
41019                 var add =   d.data;
41020                 if (typeof(add.end_dt) == 'undefined')  {
41021                     Roo.log("Missing End time in calendar data: ");
41022                     Roo.log(d);
41023                     return;
41024                 }
41025                 if (typeof(add.start_dt) == 'undefined')  {
41026                     Roo.log("Missing Start time in calendar data: ");
41027                     Roo.log(d);
41028                     return;
41029                 }
41030                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41031                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41032                 add.id = add.id || d.id;
41033                 add.title = add.title || '??';
41034                 
41035                 this.addItem(d);
41036                 
41037              
41038             },this);
41039         }
41040         
41041         this.renderEvents();
41042     }
41043     
41044
41045 });
41046 /*
41047  grid : {
41048                 xtype: 'Grid',
41049                 xns: Roo.grid,
41050                 listeners : {
41051                     render : function ()
41052                     {
41053                         _this.grid = this;
41054                         
41055                         if (!this.view.el.hasClass('course-timesheet')) {
41056                             this.view.el.addClass('course-timesheet');
41057                         }
41058                         if (this.tsStyle) {
41059                             this.ds.load({});
41060                             return; 
41061                         }
41062                         Roo.log('width');
41063                         Roo.log(_this.grid.view.el.getWidth());
41064                         
41065                         
41066                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41067                             '.course-timesheet .x-grid-row' : {
41068                                 height: '80px'
41069                             },
41070                             '.x-grid-row td' : {
41071                                 'vertical-align' : 0
41072                             },
41073                             '.course-edit-link' : {
41074                                 'color' : 'blue',
41075                                 'text-overflow' : 'ellipsis',
41076                                 'overflow' : 'hidden',
41077                                 'white-space' : 'nowrap',
41078                                 'cursor' : 'pointer'
41079                             },
41080                             '.sub-link' : {
41081                                 'color' : 'green'
41082                             },
41083                             '.de-act-sup-link' : {
41084                                 'color' : 'purple',
41085                                 'text-decoration' : 'line-through'
41086                             },
41087                             '.de-act-link' : {
41088                                 'color' : 'red',
41089                                 'text-decoration' : 'line-through'
41090                             },
41091                             '.course-timesheet .course-highlight' : {
41092                                 'border-top-style': 'dashed !important',
41093                                 'border-bottom-bottom': 'dashed !important'
41094                             },
41095                             '.course-timesheet .course-item' : {
41096                                 'font-family'   : 'tahoma, arial, helvetica',
41097                                 'font-size'     : '11px',
41098                                 'overflow'      : 'hidden',
41099                                 'padding-left'  : '10px',
41100                                 'padding-right' : '10px',
41101                                 'padding-top' : '10px' 
41102                             }
41103                             
41104                         }, Roo.id());
41105                                 this.ds.load({});
41106                     }
41107                 },
41108                 autoWidth : true,
41109                 monitorWindowResize : false,
41110                 cellrenderer : function(v,x,r)
41111                 {
41112                     return v;
41113                 },
41114                 sm : {
41115                     xtype: 'CellSelectionModel',
41116                     xns: Roo.grid
41117                 },
41118                 dataSource : {
41119                     xtype: 'Store',
41120                     xns: Roo.data,
41121                     listeners : {
41122                         beforeload : function (_self, options)
41123                         {
41124                             options.params = options.params || {};
41125                             options.params._month = _this.monthField.getValue();
41126                             options.params.limit = 9999;
41127                             options.params['sort'] = 'when_dt';    
41128                             options.params['dir'] = 'ASC';    
41129                             this.proxy.loadResponse = this.loadResponse;
41130                             Roo.log("load?");
41131                             //this.addColumns();
41132                         },
41133                         load : function (_self, records, options)
41134                         {
41135                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41136                                 // if you click on the translation.. you can edit it...
41137                                 var el = Roo.get(this);
41138                                 var id = el.dom.getAttribute('data-id');
41139                                 var d = el.dom.getAttribute('data-date');
41140                                 var t = el.dom.getAttribute('data-time');
41141                                 //var id = this.child('span').dom.textContent;
41142                                 
41143                                 //Roo.log(this);
41144                                 Pman.Dialog.CourseCalendar.show({
41145                                     id : id,
41146                                     when_d : d,
41147                                     when_t : t,
41148                                     productitem_active : id ? 1 : 0
41149                                 }, function() {
41150                                     _this.grid.ds.load({});
41151                                 });
41152                            
41153                            });
41154                            
41155                            _this.panel.fireEvent('resize', [ '', '' ]);
41156                         }
41157                     },
41158                     loadResponse : function(o, success, response){
41159                             // this is overridden on before load..
41160                             
41161                             Roo.log("our code?");       
41162                             //Roo.log(success);
41163                             //Roo.log(response)
41164                             delete this.activeRequest;
41165                             if(!success){
41166                                 this.fireEvent("loadexception", this, o, response);
41167                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41168                                 return;
41169                             }
41170                             var result;
41171                             try {
41172                                 result = o.reader.read(response);
41173                             }catch(e){
41174                                 Roo.log("load exception?");
41175                                 this.fireEvent("loadexception", this, o, response, e);
41176                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41177                                 return;
41178                             }
41179                             Roo.log("ready...");        
41180                             // loop through result.records;
41181                             // and set this.tdate[date] = [] << array of records..
41182                             _this.tdata  = {};
41183                             Roo.each(result.records, function(r){
41184                                 //Roo.log(r.data);
41185                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41186                                     _this.tdata[r.data.when_dt.format('j')] = [];
41187                                 }
41188                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41189                             });
41190                             
41191                             //Roo.log(_this.tdata);
41192                             
41193                             result.records = [];
41194                             result.totalRecords = 6;
41195                     
41196                             // let's generate some duumy records for the rows.
41197                             //var st = _this.dateField.getValue();
41198                             
41199                             // work out monday..
41200                             //st = st.add(Date.DAY, -1 * st.format('w'));
41201                             
41202                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41203                             
41204                             var firstOfMonth = date.getFirstDayOfMonth();
41205                             var days = date.getDaysInMonth();
41206                             var d = 1;
41207                             var firstAdded = false;
41208                             for (var i = 0; i < result.totalRecords ; i++) {
41209                                 //var d= st.add(Date.DAY, i);
41210                                 var row = {};
41211                                 var added = 0;
41212                                 for(var w = 0 ; w < 7 ; w++){
41213                                     if(!firstAdded && firstOfMonth != w){
41214                                         continue;
41215                                     }
41216                                     if(d > days){
41217                                         continue;
41218                                     }
41219                                     firstAdded = true;
41220                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41221                                     row['weekday'+w] = String.format(
41222                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41223                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41224                                                     d,
41225                                                     date.format('Y-m-')+dd
41226                                                 );
41227                                     added++;
41228                                     if(typeof(_this.tdata[d]) != 'undefined'){
41229                                         Roo.each(_this.tdata[d], function(r){
41230                                             var is_sub = '';
41231                                             var deactive = '';
41232                                             var id = r.id;
41233                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41234                                             if(r.parent_id*1>0){
41235                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41236                                                 id = r.parent_id;
41237                                             }
41238                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41239                                                 deactive = 'de-act-link';
41240                                             }
41241                                             
41242                                             row['weekday'+w] += String.format(
41243                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41244                                                     id, //0
41245                                                     r.product_id_name, //1
41246                                                     r.when_dt.format('h:ia'), //2
41247                                                     is_sub, //3
41248                                                     deactive, //4
41249                                                     desc // 5
41250                                             );
41251                                         });
41252                                     }
41253                                     d++;
41254                                 }
41255                                 
41256                                 // only do this if something added..
41257                                 if(added > 0){ 
41258                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41259                                 }
41260                                 
41261                                 
41262                                 // push it twice. (second one with an hour..
41263                                 
41264                             }
41265                             //Roo.log(result);
41266                             this.fireEvent("load", this, o, o.request.arg);
41267                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41268                         },
41269                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41270                     proxy : {
41271                         xtype: 'HttpProxy',
41272                         xns: Roo.data,
41273                         method : 'GET',
41274                         url : baseURL + '/Roo/Shop_course.php'
41275                     },
41276                     reader : {
41277                         xtype: 'JsonReader',
41278                         xns: Roo.data,
41279                         id : 'id',
41280                         fields : [
41281                             {
41282                                 'name': 'id',
41283                                 'type': 'int'
41284                             },
41285                             {
41286                                 'name': 'when_dt',
41287                                 'type': 'string'
41288                             },
41289                             {
41290                                 'name': 'end_dt',
41291                                 'type': 'string'
41292                             },
41293                             {
41294                                 'name': 'parent_id',
41295                                 'type': 'int'
41296                             },
41297                             {
41298                                 'name': 'product_id',
41299                                 'type': 'int'
41300                             },
41301                             {
41302                                 'name': 'productitem_id',
41303                                 'type': 'int'
41304                             },
41305                             {
41306                                 'name': 'guid',
41307                                 'type': 'int'
41308                             }
41309                         ]
41310                     }
41311                 },
41312                 toolbar : {
41313                     xtype: 'Toolbar',
41314                     xns: Roo,
41315                     items : [
41316                         {
41317                             xtype: 'Button',
41318                             xns: Roo.Toolbar,
41319                             listeners : {
41320                                 click : function (_self, e)
41321                                 {
41322                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41323                                     sd.setMonth(sd.getMonth()-1);
41324                                     _this.monthField.setValue(sd.format('Y-m-d'));
41325                                     _this.grid.ds.load({});
41326                                 }
41327                             },
41328                             text : "Back"
41329                         },
41330                         {
41331                             xtype: 'Separator',
41332                             xns: Roo.Toolbar
41333                         },
41334                         {
41335                             xtype: 'MonthField',
41336                             xns: Roo.form,
41337                             listeners : {
41338                                 render : function (_self)
41339                                 {
41340                                     _this.monthField = _self;
41341                                    // _this.monthField.set  today
41342                                 },
41343                                 select : function (combo, date)
41344                                 {
41345                                     _this.grid.ds.load({});
41346                                 }
41347                             },
41348                             value : (function() { return new Date(); })()
41349                         },
41350                         {
41351                             xtype: 'Separator',
41352                             xns: Roo.Toolbar
41353                         },
41354                         {
41355                             xtype: 'TextItem',
41356                             xns: Roo.Toolbar,
41357                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41358                         },
41359                         {
41360                             xtype: 'Fill',
41361                             xns: Roo.Toolbar
41362                         },
41363                         {
41364                             xtype: 'Button',
41365                             xns: Roo.Toolbar,
41366                             listeners : {
41367                                 click : function (_self, e)
41368                                 {
41369                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41370                                     sd.setMonth(sd.getMonth()+1);
41371                                     _this.monthField.setValue(sd.format('Y-m-d'));
41372                                     _this.grid.ds.load({});
41373                                 }
41374                             },
41375                             text : "Next"
41376                         }
41377                     ]
41378                 },
41379                  
41380             }
41381         };
41382         
41383         *//*
41384  * Based on:
41385  * Ext JS Library 1.1.1
41386  * Copyright(c) 2006-2007, Ext JS, LLC.
41387  *
41388  * Originally Released Under LGPL - original licence link has changed is not relivant.
41389  *
41390  * Fork - LGPL
41391  * <script type="text/javascript">
41392  */
41393  
41394 /**
41395  * @class Roo.LoadMask
41396  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41397  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41398  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41399  * element's UpdateManager load indicator and will be destroyed after the initial load.
41400  * @constructor
41401  * Create a new LoadMask
41402  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41403  * @param {Object} config The config object
41404  */
41405 Roo.LoadMask = function(el, config){
41406     this.el = Roo.get(el);
41407     Roo.apply(this, config);
41408     if(this.store){
41409         this.store.on('beforeload', this.onBeforeLoad, this);
41410         this.store.on('load', this.onLoad, this);
41411         this.store.on('loadexception', this.onLoadException, this);
41412         this.removeMask = false;
41413     }else{
41414         var um = this.el.getUpdateManager();
41415         um.showLoadIndicator = false; // disable the default indicator
41416         um.on('beforeupdate', this.onBeforeLoad, this);
41417         um.on('update', this.onLoad, this);
41418         um.on('failure', this.onLoad, this);
41419         this.removeMask = true;
41420     }
41421 };
41422
41423 Roo.LoadMask.prototype = {
41424     /**
41425      * @cfg {Boolean} removeMask
41426      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41427      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41428      */
41429     /**
41430      * @cfg {String} msg
41431      * The text to display in a centered loading message box (defaults to 'Loading...')
41432      */
41433     msg : 'Loading...',
41434     /**
41435      * @cfg {String} msgCls
41436      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41437      */
41438     msgCls : 'x-mask-loading',
41439
41440     /**
41441      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41442      * @type Boolean
41443      */
41444     disabled: false,
41445
41446     /**
41447      * Disables the mask to prevent it from being displayed
41448      */
41449     disable : function(){
41450        this.disabled = true;
41451     },
41452
41453     /**
41454      * Enables the mask so that it can be displayed
41455      */
41456     enable : function(){
41457         this.disabled = false;
41458     },
41459     
41460     onLoadException : function()
41461     {
41462         Roo.log(arguments);
41463         
41464         if (typeof(arguments[3]) != 'undefined') {
41465             Roo.MessageBox.alert("Error loading",arguments[3]);
41466         } 
41467         /*
41468         try {
41469             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41470                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41471             }   
41472         } catch(e) {
41473             
41474         }
41475         */
41476     
41477         
41478         
41479         this.el.unmask(this.removeMask);
41480     },
41481     // private
41482     onLoad : function()
41483     {
41484         this.el.unmask(this.removeMask);
41485     },
41486
41487     // private
41488     onBeforeLoad : function(){
41489         if(!this.disabled){
41490             this.el.mask(this.msg, this.msgCls);
41491         }
41492     },
41493
41494     // private
41495     destroy : function(){
41496         if(this.store){
41497             this.store.un('beforeload', this.onBeforeLoad, this);
41498             this.store.un('load', this.onLoad, this);
41499             this.store.un('loadexception', this.onLoadException, this);
41500         }else{
41501             var um = this.el.getUpdateManager();
41502             um.un('beforeupdate', this.onBeforeLoad, this);
41503             um.un('update', this.onLoad, this);
41504             um.un('failure', this.onLoad, this);
41505         }
41506     }
41507 };/*
41508  * Based on:
41509  * Ext JS Library 1.1.1
41510  * Copyright(c) 2006-2007, Ext JS, LLC.
41511  *
41512  * Originally Released Under LGPL - original licence link has changed is not relivant.
41513  *
41514  * Fork - LGPL
41515  * <script type="text/javascript">
41516  */
41517
41518
41519 /**
41520  * @class Roo.XTemplate
41521  * @extends Roo.Template
41522  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
41523 <pre><code>
41524 var t = new Roo.XTemplate(
41525         '&lt;select name="{name}"&gt;',
41526                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
41527         '&lt;/select&gt;'
41528 );
41529  
41530 // then append, applying the master template values
41531  </code></pre>
41532  *
41533  * Supported features:
41534  *
41535  *  Tags:
41536
41537 <pre><code>
41538       {a_variable} - output encoded.
41539       {a_variable.format:("Y-m-d")} - call a method on the variable
41540       {a_variable:raw} - unencoded output
41541       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
41542       {a_variable:this.method_on_template(...)} - call a method on the template object.
41543  
41544 </code></pre>
41545  *  The tpl tag:
41546 <pre><code>
41547         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
41548         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
41549         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
41550         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
41551   
41552         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
41553         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
41554 </code></pre>
41555  *      
41556  */
41557 Roo.XTemplate = function()
41558 {
41559     Roo.XTemplate.superclass.constructor.apply(this, arguments);
41560     if (this.html) {
41561         this.compile();
41562     }
41563 };
41564
41565
41566 Roo.extend(Roo.XTemplate, Roo.Template, {
41567
41568     /**
41569      * The various sub templates
41570      */
41571     tpls : false,
41572     /**
41573      *
41574      * basic tag replacing syntax
41575      * WORD:WORD()
41576      *
41577      * // you can fake an object call by doing this
41578      *  x.t:(test,tesT) 
41579      * 
41580      */
41581     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
41582
41583     /**
41584      * compile the template
41585      *
41586      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
41587      *
41588      */
41589     compile: function()
41590     {
41591         var s = this.html;
41592      
41593         s = ['<tpl>', s, '</tpl>'].join('');
41594     
41595         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
41596             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
41597             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
41598             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
41599             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
41600             m,
41601             id     = 0,
41602             tpls   = [];
41603     
41604         while(true == !!(m = s.match(re))){
41605             var forMatch   = m[0].match(nameRe),
41606                 ifMatch   = m[0].match(ifRe),
41607                 execMatch   = m[0].match(execRe),
41608                 namedMatch   = m[0].match(namedRe),
41609                 
41610                 exp  = null, 
41611                 fn   = null,
41612                 exec = null,
41613                 name = forMatch && forMatch[1] ? forMatch[1] : '';
41614                 
41615             if (ifMatch) {
41616                 // if - puts fn into test..
41617                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
41618                 if(exp){
41619                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
41620                 }
41621             }
41622             
41623             if (execMatch) {
41624                 // exec - calls a function... returns empty if true is  returned.
41625                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
41626                 if(exp){
41627                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
41628                 }
41629             }
41630             
41631             
41632             if (name) {
41633                 // for = 
41634                 switch(name){
41635                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
41636                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
41637                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
41638                 }
41639             }
41640             var uid = namedMatch ? namedMatch[1] : id;
41641             
41642             
41643             tpls.push({
41644                 id:     namedMatch ? namedMatch[1] : id,
41645                 target: name,
41646                 exec:   exec,
41647                 test:   fn,
41648                 body:   m[1] || ''
41649             });
41650             if (namedMatch) {
41651                 s = s.replace(m[0], '');
41652             } else { 
41653                 s = s.replace(m[0], '{xtpl'+ id + '}');
41654             }
41655             ++id;
41656         }
41657         this.tpls = [];
41658         for(var i = tpls.length-1; i >= 0; --i){
41659             this.compileTpl(tpls[i]);
41660             this.tpls[tpls[i].id] = tpls[i];
41661         }
41662         this.master = tpls[tpls.length-1];
41663         return this;
41664     },
41665     /**
41666      * same as applyTemplate, except it's done to one of the subTemplates
41667      * when using named templates, you can do:
41668      *
41669      * var str = pl.applySubTemplate('your-name', values);
41670      *
41671      * 
41672      * @param {Number} id of the template
41673      * @param {Object} values to apply to template
41674      * @param {Object} parent (normaly the instance of this object)
41675      */
41676     applySubTemplate : function(id, values, parent)
41677     {
41678         
41679         
41680         var t = this.tpls[id];
41681         
41682         
41683         try { 
41684             if(t.test && !t.test.call(this, values, parent)){
41685                 return '';
41686             }
41687         } catch(e) {
41688             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
41689             Roo.log(e.toString());
41690             Roo.log(t.test);
41691             return ''
41692         }
41693         try { 
41694             
41695             if(t.exec && t.exec.call(this, values, parent)){
41696                 return '';
41697             }
41698         } catch(e) {
41699             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
41700             Roo.log(e.toString());
41701             Roo.log(t.exec);
41702             return ''
41703         }
41704         try {
41705             var vs = t.target ? t.target.call(this, values, parent) : values;
41706             parent = t.target ? values : parent;
41707             if(t.target && vs instanceof Array){
41708                 var buf = [];
41709                 for(var i = 0, len = vs.length; i < len; i++){
41710                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
41711                 }
41712                 return buf.join('');
41713             }
41714             return t.compiled.call(this, vs, parent);
41715         } catch (e) {
41716             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
41717             Roo.log(e.toString());
41718             Roo.log(t.compiled);
41719             return '';
41720         }
41721     },
41722
41723     compileTpl : function(tpl)
41724     {
41725         var fm = Roo.util.Format;
41726         var useF = this.disableFormats !== true;
41727         var sep = Roo.isGecko ? "+" : ",";
41728         var undef = function(str) {
41729             Roo.log("Property not found :"  + str);
41730             return '';
41731         };
41732         
41733         var fn = function(m, name, format, args)
41734         {
41735             //Roo.log(arguments);
41736             args = args ? args.replace(/\\'/g,"'") : args;
41737             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
41738             if (typeof(format) == 'undefined') {
41739                 format= 'htmlEncode';
41740             }
41741             if (format == 'raw' ) {
41742                 format = false;
41743             }
41744             
41745             if(name.substr(0, 4) == 'xtpl'){
41746                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
41747             }
41748             
41749             // build an array of options to determine if value is undefined..
41750             
41751             // basically get 'xxxx.yyyy' then do
41752             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
41753             //    (function () { Roo.log("Property not found"); return ''; })() :
41754             //    ......
41755             
41756             var udef_ar = [];
41757             var lookfor = '';
41758             Roo.each(name.split('.'), function(st) {
41759                 lookfor += (lookfor.length ? '.': '') + st;
41760                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
41761             });
41762             
41763             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
41764             
41765             
41766             if(format && useF){
41767                 
41768                 args = args ? ',' + args : "";
41769                  
41770                 if(format.substr(0, 5) != "this."){
41771                     format = "fm." + format + '(';
41772                 }else{
41773                     format = 'this.call("'+ format.substr(5) + '", ';
41774                     args = ", values";
41775                 }
41776                 
41777                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
41778             }
41779              
41780             if (args.length) {
41781                 // called with xxyx.yuu:(test,test)
41782                 // change to ()
41783                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
41784             }
41785             // raw.. - :raw modifier..
41786             return "'"+ sep + udef_st  + name + ")"+sep+"'";
41787             
41788         };
41789         var body;
41790         // branched to use + in gecko and [].join() in others
41791         if(Roo.isGecko){
41792             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
41793                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
41794                     "';};};";
41795         }else{
41796             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
41797             body.push(tpl.body.replace(/(\r\n|\n)/g,
41798                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
41799             body.push("'].join('');};};");
41800             body = body.join('');
41801         }
41802         
41803         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
41804        
41805         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
41806         eval(body);
41807         
41808         return this;
41809     },
41810
41811     applyTemplate : function(values){
41812         return this.master.compiled.call(this, values, {});
41813         //var s = this.subs;
41814     },
41815
41816     apply : function(){
41817         return this.applyTemplate.apply(this, arguments);
41818     }
41819
41820  });
41821
41822 Roo.XTemplate.from = function(el){
41823     el = Roo.getDom(el);
41824     return new Roo.XTemplate(el.value || el.innerHTML);
41825 };